Skip to content

Commit 64126f8

Browse files
Draft of #327 code fixes
1 parent 60fb8d5 commit 64126f8

File tree

6 files changed

+123
-43
lines changed

6 files changed

+123
-43
lines changed

clang/include/clang/3C/3CGlobalOptions.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ extern bool EnablePropThruIType;
2525
extern bool ConsiderAllocUnsafe;
2626
extern bool AllTypes;
2727
extern bool NewSolver;
28-
extern std::string WorkingDir;
2928
extern std::string BaseDir;
3029
extern bool AllowSourcesOutsideBaseDir;
3130
extern std::vector<std::string> AllocatorFunctions;

clang/include/clang/3C/Utils.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,19 @@ bool hasFunctionBody(clang::Decl *D);
104104

105105
std::string getStorageQualifierString(clang::Decl *D);
106106

107-
void getAbsoluteFilePath(std::string FileName, std::string &AbsoluteFp);
107+
// Use this version for user input that has not yet been validated.
108+
std::error_code tryGetCanonicalFilePath(const std::string &FileName,
109+
std::string &AbsoluteFp);
110+
111+
// This variant asserts success. It may be used for convenience for files we are
112+
// already accessing and thus believe should exist, modulo race conditions and
113+
// the like.
114+
void getCanonicalFilePath(const std::string &FileName, std::string &AbsoluteFp);
115+
116+
// This compares entire path components: it's smart enough to know that "foo.c"
117+
// does not start with "foo". It's not smart about anything else, so you may
118+
// wish to put both paths through getCanonicalFilePath first.
119+
bool filePathStartsWith(const std::string &Path, const std::string &Prefix);
108120

109121
bool isNULLExpression(clang::Expr *E, clang::ASTContext &C);
110122

@@ -147,6 +159,9 @@ bool isCastSafe(clang::QualType DstType, clang::QualType SrcType);
147159

148160
// Check if the provided file path belongs to the input project
149161
// and can be rewritten.
162+
//
163+
// For accurate results, the path must have gone through getCanonicalFilePath.
164+
// Note that the file name of a PersistentSourceLoc is always canonical.
150165
bool canWrite(const std::string &FilePath);
151166

152167
// Check if the provided variable has void as one of its type.

clang/lib/3C/3C.cpp

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ std::string StatsOutputJson;
4949
std::string WildPtrInfoJson;
5050
std::string PerWildPtrInfoJson;
5151
bool AllTypes;
52-
std::string WorkingDir;
5352
std::string BaseDir;
5453
bool AllowSourcesOutsideBaseDir;
5554
bool AddCheckedRegions;
@@ -250,32 +249,79 @@ _3CInterface::_3CInterface(const struct _3COptions &CCopt,
250249

251250
ConstraintsBuilt = false;
252251

253-
SmallString<256> Cp;
254-
if (std::error_code Ec = sys::fs::current_path(Cp)) {
255-
errs() << "could not get current working dir\n";
256-
assert(false && "Unable to get determine working directory.");
252+
int NumOutputOptions = 0;
253+
if (CCopt.OutputPostfix != "-")
254+
NumOutputOptions++;
255+
if (CCopt.OutputDir != "")
256+
NumOutputOptions++;
257+
if (NumOutputOptions > 1) {
258+
errs() << "3C initialization error: Cannot use both -output-postfix and "
259+
"-output-dir\n";
260+
Failed = true;
261+
return;
262+
}
263+
if (NumOutputOptions == 0 && SourceFileList.size() > 1) {
264+
errs() << "3C initialization error: Cannot specify more than one input "
265+
"file when output is to stdout\n";
266+
Failed = true;
267+
return;
257268
}
258-
WorkingDir = Cp.str();
269+
270+
std::string TmpPath;
271+
std::error_code EC;
259272

260273
if (BaseDir.empty()) {
261-
BaseDir = WorkingDir;
262-
} else {
263-
// Get the absolute path of the base directory.
264-
std::string TmpPath = BaseDir;
265-
getAbsoluteFilePath(BaseDir, TmpPath);
266-
BaseDir = TmpPath;
274+
BaseDir = ".";
275+
}
276+
277+
// Get the canonical path of the base directory.
278+
TmpPath = BaseDir;
279+
EC = tryGetCanonicalFilePath(BaseDir, TmpPath);
280+
if (EC) {
281+
errs() << "3C initialization error: Failed to canonicalize base directory "
282+
"\"" << BaseDir << "\": " << EC.message() << "\n";
283+
Failed = true;
284+
return;
285+
}
286+
BaseDir = TmpPath;
287+
288+
if (!OutputDir.empty()) {
289+
// tryGetCanonicalFilePath will fail if the output dir doesn't exist yet, so
290+
// create it first.
291+
EC = llvm::sys::fs::create_directories(OutputDir);
292+
if (EC) {
293+
errs() << "3C initialization error: Failed to create output directory \""
294+
<< OutputDir << "\": " << EC.message() << "\n";
295+
Failed = true;
296+
return;
297+
}
298+
TmpPath = OutputDir;
299+
EC = tryGetCanonicalFilePath(OutputDir, TmpPath);
300+
if (EC) {
301+
errs() << "3C initialization error: Failed to canonicalize output "
302+
"directory \"" << OutputDir << "\": " << EC.message() << "\n";
303+
Failed = true;
304+
return;
305+
}
306+
OutputDir = TmpPath;
267307
}
268308

269309
SourceFiles = SourceFileList;
270310

271311
bool SawInputOutsideBaseDir = false;
272312
for (const auto &S : SourceFiles) {
273313
std::string AbsPath;
274-
getAbsoluteFilePath(S, AbsPath);
314+
EC = tryGetCanonicalFilePath(S, AbsPath);
315+
if (EC) {
316+
errs() << "3C initialization error: Failed to canonicalize source file "
317+
"path \"" << S << "\": " << EC.message() << "\n";
318+
Failed = true;
319+
continue;
320+
}
275321
FilePaths.insert(AbsPath);
276-
if (AbsPath.substr(0, BaseDir.size()) != BaseDir) {
322+
if (!filePathStartsWith(AbsPath, BaseDir)) {
277323
errs()
278-
<< "3C command-line usage "
324+
<< "3C initialization "
279325
<< (OutputDir != "" || !AllowSourcesOutsideBaseDir ? "error"
280326
: "warning")
281327
<< ": File \"" << AbsPath
@@ -299,22 +345,6 @@ _3CInterface::_3CInterface(const struct _3COptions &CCopt,
299345
}
300346

301347
CurrCompDB = CompDB;
302-
303-
int NumOutputOptions = 0;
304-
if (CCopt.OutputPostfix != "-")
305-
NumOutputOptions++;
306-
if (CCopt.OutputDir != "")
307-
NumOutputOptions++;
308-
if (NumOutputOptions > 1) {
309-
errs() << "3C command-line usage error: Cannot use both -output-postfix "
310-
"and -output-dir\n";
311-
Failed = true;
312-
}
313-
if (NumOutputOptions == 0 && SourceFileList.size() > 1) {
314-
errs() << "3C command-line usage error: Cannot specify more than one input "
315-
"file when output is to stdout\n";
316-
Failed = true;
317-
}
318348
}
319349

320350
bool _3CInterface::buildInitialConstraints() {

clang/lib/3C/PersistentSourceLoc.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ PersistentSourceLoc PersistentSourceLoc::mkPSL(clang::SourceRange SR,
7070
std::string FeAbsS = "";
7171
if (Fe != nullptr)
7272
ToConv = Fe->getName();
73-
getAbsoluteFilePath(ToConv, FeAbsS);
73+
getCanonicalFilePath(ToConv, FeAbsS);
7474
Fn = sys::path::remove_leading_dotslash(FeAbsS);
7575
}
7676
PersistentSourceLoc PSL(Fn, FESL.getExpansionLineNumber(),

clang/lib/3C/RewriteUtils.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ static void emit(Rewriter &R, ASTContext &C) {
164164

165165
// Check whether we are allowed to write this file.
166166
std::string FeAbsS = "";
167-
getAbsoluteFilePath(FE->getName(), FeAbsS);
167+
getCanonicalFilePath(FE->getName(), FeAbsS);
168168
if (!canWrite(FeAbsS)) {
169169
DiagnosticsEngine &DE = C.getDiagnostics();
170170
unsigned ID = DE.getCustomDiagID(
@@ -216,8 +216,13 @@ static void emit(Rewriter &R, ASTContext &C) {
216216
assert(OutputDir != "");
217217
// If this does not hold when OutputDir is set, it should have been a
218218
// fatal error in the _3CInterface constructor.
219-
assert(FeAbsS.substr(0, BaseDir.size()) == BaseDir);
220-
NFile = OutputDir + FeAbsS.substr(BaseDir.size());
219+
assert(filePathStartsWith(FeAbsS, BaseDir));
220+
// replace_path_prefix is not smart about separators, but this should be
221+
// OK because getCanonicalFilePath should ensure that neither BaseDir
222+
// nor OutputDir has a trailing separator.
223+
SmallString<255> Tmp(FeAbsS);
224+
llvm::sys::path::replace_path_prefix(Tmp, BaseDir, OutputDir);
225+
NFile = Tmp.str();
221226
EC = llvm::sys::fs::create_directories(sys::path::parent_path(NFile));
222227
if (EC) {
223228
DiagnosticsEngine &DE = C.getDiagnostics();

clang/lib/3C/Utils.cpp

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "clang/3C/3CGlobalOptions.h"
1313
#include "clang/3C/ConstraintVariables.h"
1414
#include "llvm/Support/Path.h"
15+
#include <errno.h>
1516

1617
using namespace llvm;
1718
using namespace clang;
@@ -142,12 +143,44 @@ bool isNULLExpression(clang::Expr *E, ASTContext &C) {
142143
E->isNullPointerConstant(C, Expr::NPC_ValueDependentIsNotNull);
143144
}
144145

145-
void getAbsoluteFilePath(std::string FileName, std::string &AbsoluteFp) {
146+
std::error_code tryGetCanonicalFilePath(const std::string &FileName, std::string &AbsoluteFp) {
146147
// Get absolute path of the provided file
147148
// returns true if successful else false.
148-
SmallString<255> AbsPath(FileName);
149-
llvm::sys::fs::make_absolute(WorkingDir, AbsPath);
149+
SmallString<255> AbsPath;
150+
std::error_code EC;
151+
if (FileName.empty()) {
152+
// Strangely, llvm::sys::fs::real_path successfully returns the empty string
153+
// in this case.
154+
EC = std::error_code(ENOENT, std::generic_category());
155+
} else {
156+
EC = llvm::sys::fs::real_path(FileName, AbsPath);
157+
}
158+
if (EC) {
159+
return EC;
160+
}
150161
AbsoluteFp = AbsPath.str();
162+
return EC;
163+
}
164+
165+
void getCanonicalFilePath(const std::string &FileName, std::string &AbsoluteFp) {
166+
std::error_code EC = tryGetCanonicalFilePath(FileName, AbsoluteFp);
167+
assert(!EC && "tryGetCanonicalFilePath failed");
168+
}
169+
170+
bool filePathStartsWith(const std::string &Path, const std::string &Prefix) {
171+
// If the path exactly equals the prefix, don't ruin it by appending a
172+
// separator to the prefix. (This may not happen in 3C yet, but let's get it
173+
// right.)
174+
if (Prefix.empty() || Path == Prefix) {
175+
return true;
176+
}
177+
StringRef separator = llvm::sys::path::get_separator();
178+
std::string PrefixWithTrailingSeparator = Prefix;
179+
if (!StringRef(Prefix).endswith(separator)) {
180+
PrefixWithTrailingSeparator += separator;
181+
}
182+
return Path.substr(0, PrefixWithTrailingSeparator.size()) ==
183+
PrefixWithTrailingSeparator;
151184
}
152185

153186
bool functionHasVarArgs(clang::FunctionDecl *FD) {
@@ -337,9 +370,7 @@ bool canWrite(const std::string &FilePath) {
337370
return true;
338371
// Get the absolute path of the file and check that
339372
// the file path starts with the base directory.
340-
std::string FileAbsPath = FilePath;
341-
getAbsoluteFilePath(FilePath, FileAbsPath);
342-
return FileAbsPath.rfind(BaseDir, 0) == 0;
373+
return filePathStartsWith(FilePath, BaseDir);
343374
}
344375

345376
bool isInSysHeader(clang::Decl *D) {

0 commit comments

Comments
 (0)