Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1987 lines
53 KiB

  1. /*
  2. A hack to look in source files for "sharing hazards",
  3. very simple code patterns that should be examined to determine
  4. if multiple versions of the code can live "side-by-side".
  5. Problems are explicit unversioned sharing.
  6. Registry writes.
  7. File system writes.
  8. Naming of objects (kernel objects) -- open or create.
  9. */
  10. /*
  11. UNDONE and BUGS
  12. preprocessor directives are ignored
  13. the behavior of mbcs in strings and comments is not quite determinate
  14. there is not yet anyway to quash warnings
  15. backslash line continuation is not implemented, nor are trigraphs (trigraphs can produce
  16. # and \ for preprocessor directives or line continuation)
  17. no unicode support
  18. no \u support
  19. not quite tolerant of embedded nuls in file, but almost now
  20. some inefficiency
  21. some uncleanliness
  22. the reuse of the comment stripper isn't quite right
  23. the global line tracking isn't ver effiecent, but works
  24. @owner a-JayK, JayKrell
  25. */
  26. /\
  27. *
  28. BUG line continuation
  29. *\
  30. /
  31. /\
  32. / not honored
  33. /* The VC6 editor does not highlight the above correctly, but
  34. the compiler implements it correctly. */
  35. #pragma warning(disable:4786) /* long names in debug info truncated */
  36. #pragma warning(disable:4018) /* signed/unsigned */
  37. #include "MinMax.h"
  38. /* notice how good VC's support of trigraphs and line continuation is */
  39. ??=include <st??/
  40. dio.h>
  41. #include <stdlib.h>
  42. #include <string.h>
  43. #include <ctype.h>
  44. #incl\
  45. ude "windows.h"
  46. #define NUMBER_OF(x) (sizeof(x)/sizeof((x)[0]))
  47. #include "Casts.h"
  48. void CheckHresult(HRESULT);
  49. void ThrowHresult(HRESULT);
  50. #include "Handle.h"
  51. #include "comdef.h"
  52. #include <algorithm>
  53. #include <string>
  54. #include <vector>
  55. #include <set>
  56. #include <stack>
  57. #include <iostream>
  58. typedef struct HazardousFunction HazardousFunction;
  59. typedef int (__cdecl* QsortFunction)(const void*, const void*);
  60. typedef int (__cdecl* BsearchFunction)(const void*, const void*);
  61. class CLine;
  62. class CClass;
  63. enum ETokenType;
  64. /* get msvcrt.dll wildcard processing, doesn't work with libc.lib */
  65. //extern
  66. //#if defined(__cplusplus)
  67. //"C"
  68. //#endif
  69. //int _dowildcard = 1;
  70. const char* CheckCreateObject(const CClass&);
  71. const char* CheckCreateFile(const CClass&);
  72. const char* CheckRegOpenEx(const CClass&);
  73. void PrintOpenComment();
  74. void PrintCloseComment();
  75. void PrintSeperator();
  76. unsigned short StringLeadingTwoCharsTo14Bits(const char* s);
  77. void __stdcall CheckHresult(HRESULT hr)
  78. {
  79. if (FAILED(hr))
  80. ThrowHresult(hr);
  81. }
  82. void __stdcall ThrowHresult(HRESULT hr)
  83. {
  84. throw _com_error(hr, NULL);
  85. }
  86. const char banner[] = "SharingHazardCheck version " __DATE__ " " __TIME__ "\n";
  87. const char usage[]=
  88. "%s [options directories files]\n"
  89. "version: " __DATE__ " " __TIME__ "\n"
  90. "\n"
  91. "Options may appear in any order; command line is order independent\n"
  92. "Wildcards are accepted for filenames. Wildcards never match directories.\n"
  93. "\n"
  94. "default output format\n"
  95. " file(line):reason\n"
  96. "-recurse\n"
  97. "-print-line\n"
  98. " prints line of code with offending function after file(line):reason\n"
  99. "-print-statement\n"
  100. " print statement containing offending function after file(line):reason\n"
  101. " supersedes -print-line\n"
  102. //"-print-context-lines:n\n"
  103. //" -print-statement plus surrounding n lines (not implemented)\n"
  104. "-print-context-statements:n (n is 1-4, pinned at 4, 0 untested)\n"
  105. " -print-statement plus surrounding n \"statements\"\n"
  106. "file names apply across all directories recursed into\n"
  107. "wild cards apply across all directories recursed into\n"
  108. "naming a directory implies one level recursion (unless -recurse is also seen)\n"
  109. //"environment variable SHARING_HAZARD_CHECK_OPTIONS added to argv (not implemented)\n"
  110. "all directory walking happens before any output is generated, it is slow\n"
  111. "\n"
  112. "The way recursion and wildcards work might not be intuitive.\n";
  113. enum ETokenType
  114. {
  115. /* character values show up too including
  116. (), but probably not for any potentially multi char token like !=
  117. */
  118. eTokenTypeIdentifier = 128,
  119. eTokenTypeHazardousFunction,
  120. eTokenTypeStringConstant,
  121. eTokenTypeCharConstant,
  122. eTokenTypeNumber, /* floating point or integer, we don't care */
  123. eTokenTypePreprocessorDirective /* the entire line is one token */
  124. };
  125. class CLine
  126. {
  127. public:
  128. CLine() : m_start(0), m_number(1)
  129. {
  130. }
  131. const char* m_start;
  132. int m_number;
  133. };
  134. class CRange
  135. {
  136. public:
  137. const char* begin;
  138. const char* end;
  139. };
  140. typedef CRange CStatement;
  141. class CClass
  142. {
  143. public:
  144. CClass();
  145. explicit CClass(const CClass&);
  146. void operator=(const CClass&);
  147. ~CClass() { }
  148. bool OpenFile(const char*);
  149. int GetCharacter();
  150. ETokenType GetToken();
  151. /* public */
  152. const char* m_tokenText; /* only valid for identifiers */
  153. int m_tokenLength; /* only valid for identifiers */
  154. ETokenType m_eTokenType;
  155. HazardousFunction* m_hazardousFunction;
  156. /* semi private */
  157. const char* m_begin; // used to issue warnings for copied/sub scanners
  158. const char* m_str; // current position
  159. const char* m_end; // usually end of file, sometimes earlier ("just past")
  160. bool m_fPoundIsPreprocessor; // is # a preprocessor directive?
  161. int m_spacesSinceNewline; /* FUTURE deduce indentation style */
  162. bool m_fInComment; // if we return newlines within comments.. (we don't)
  163. char m_rgchUnget[16]; /* UNDONE this is bounded to like 1 right? */
  164. int m_nUnget;
  165. void NoteStatementStart(const char*);
  166. // a statement is simply code delimited by semicolons,
  167. // we are confused by if/while/do/for
  168. mutable CStatement m_statements[4];
  169. mutable unsigned m_istatement;
  170. bool ScanToCharacter(int ch);
  171. const char* ScanToFirstParameter();
  172. const char* ScanToNextParameter();
  173. const char* ScanToNthParameter(int);
  174. int CountParameters() const; // negative if unable to "parse"
  175. const char* ScanToLastParameter();
  176. const char* ScanToSecondFromLastParameter();
  177. const char* SpanEnd(const char* set) const;
  178. bool FindString(const char*) const;
  179. CLine m_line;
  180. CLine m_nextLine; /* hack.. */
  181. void Warn(const char* = "") const;
  182. void PrintCode() const;
  183. // bool m_fPrintContext;
  184. // bool m_fPrintFullStatement;
  185. bool m_fPrintCarets;
  186. void RecordStatement(int ch);
  187. void OrderStatements() const;
  188. const char* m_statementStart;
  189. char m_fullPath[MAX_PATH];
  190. private:
  191. int GetCharacter2();
  192. void UngetCharacter2(int ch);
  193. CFusionFile m_file;
  194. CFileMapping m_fileMapping;
  195. CMappedViewOfFile m_view;
  196. };
  197. class CSharingHazardCheck
  198. {
  199. public:
  200. CSharingHazardCheck();
  201. void Main(int argc, char** argv);
  202. void ProcessArgs(int argc, char** argv, std::vector<std::string>& files);
  203. int ProcessFile(const std::string&);
  204. // bool m_fRecurse;
  205. // int m_nPrintContextStatements;
  206. };
  207. int g_nPrintContextStatements = 0;
  208. bool g_fPrintFullStatement = true;
  209. bool g_fPrintLine = true;
  210. CSharingHazardCheck app;
  211. CSharingHazardCheck::CSharingHazardCheck()
  212. //:
  213. // m_fRecurse(false),
  214. // m_nPrintContextStatements(0)
  215. {
  216. }
  217. template <typename Iterator1, typename T>
  218. Iterator1 __stdcall SequenceLinearFindValue(Iterator1 begin, Iterator1 end, T value)
  219. {
  220. for ( ; begin != end && *begin != value ; ++begin)
  221. {
  222. /* nothing */
  223. }
  224. return begin;
  225. }
  226. template <typename Iterator1, typename Iterator2>
  227. long __stdcall SequenceLengthOfSpanIncluding(Iterator1 begin, Iterator1 end, Iterator2 setBegin, Iterator2 setEnd)
  228. {
  229. long result = 0;
  230. while (begin != end && SequenceLinearFindValue(setBegin, setEnd, *begin) != setEnd)
  231. {
  232. ++begin;
  233. ++result;
  234. }
  235. return result;
  236. }
  237. template <typename Iterator1, typename Iterator2>
  238. long __stdcall SequenceLengthOfSpanExcluding(Iterator1 begin, Iterator1 end, Iterator2 setBegin, Iterator2 setEnd)
  239. {
  240. long result = 0;
  241. while (begin != end && SequenceLinearFindValue(setBegin, setEnd, *begin) == setEnd)
  242. {
  243. ++begin;
  244. ++result;
  245. }
  246. return result;
  247. }
  248. #define CASE_AZ \
  249. case 'A':case 'B':case 'C':case 'D':case 'E':case 'F':case 'G':case 'H':case 'I': \
  250. case 'J':case 'K':case 'L':case 'M':case 'N':case 'O':case 'P':case 'Q':case 'R': \
  251. case 'S':case 'T':case 'U':case 'V':case 'W':case 'X':case 'Y':case 'Z'
  252. #define CASE_az \
  253. case 'a':case 'b':case 'c':case 'd':case 'e':case 'f':case 'g':case 'h':case 'i': \
  254. case 'j':case 'k':case 'l':case 'm':case 'n':case 'o':case 'p':case 'q':case 'r': \
  255. case 's':case 't':case 'u':case 'v':case 'w':case 'x':case 'y':case 'z'
  256. #define CASE_09 \
  257. case '0':case '1':case '2':case '3':case '4': \
  258. case '5':case '6':case '7':case '8':case '9'
  259. /* try to keep character set stuff somewhat centralized */
  260. #define CASE_HORIZONTAL_SPACE case ' ': case '\t'
  261. // 0x1a is control-z; it probably marks end of file, but it's pretty rare
  262. // and usually followed by end of file, so we just treat it as vertical space
  263. #define CASE_VERTICAL_SPACE case '\n': case '\r': case 0xc: case 0x1a
  264. #define CASE_SPACE CASE_HORIZONTAL_SPACE: CASE_VERTICAL_SPACE
  265. #define VERTICAL_SPACE "\n\r\xc\x1a"
  266. #define HORIZONTAL_SPACE " \t"
  267. #define SPACE HORIZONTAL_SPACE VERTICAL_SPACE
  268. bool IsVerticalSpace(int ch) { return (ch == '\n' || ch == '\r' || ch == 0xc || ch == 0x1a); }
  269. bool IsHorizontalSpace(int ch) { return (ch == ' ' || ch == '\t'); }
  270. bool IsSpace(int ch) { return IsHorizontalSpace(ch) || IsVerticalSpace(ch); }
  271. #define DIGITS10 "0123456789"
  272. #define DIGITS_EXTRA_HEX "abcdefABCDEFxX"
  273. #define DIGITS_EXTRA_TYPE "uUlLfFDd" /* not sure about fFdD for float/double */
  274. #define DIGITS_EXTRA_FLOAT "eE."
  275. const char digits10[] = DIGITS10;
  276. #define DIGITS_ALL DIGITS10 DIGITS_EXTRA_TYPE DIGITS_EXTRA_HEX DIGITS_EXTRA_FLOAT
  277. const char digitsAll[] = DIGITS_ALL;
  278. #define UPPER_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  279. #define LOWER_LETTERS "abcdefghijklmnopqrstuvwxyz"
  280. #define IDENTIFIER_CHARS UPPER_LETTERS LOWER_LETTERS DIGITS10 "_"
  281. const char upperLetters[] = UPPER_LETTERS;
  282. const char lowerLetters[] = LOWER_LETTERS;
  283. const char identifierChars[] = IDENTIFIER_CHARS;
  284. #define JAYK 0
  285. #if JAYK
  286. #if _M_IX86
  287. #define BreakPoint() __asm { int 3 }
  288. #else
  289. #define BreakPoint() DebugBreak()
  290. #endif
  291. #else
  292. #define BreakPoint() /* nothing */
  293. #endif
  294. /* actually..these don't work unless we #undef what windows.h gives us ..
  295. #define STRINGIZE_EVAL_AGAIN(name) #name
  296. #define STRINGIZE(name) STRINGIZE_EVAL_AGAIN(name)
  297. #define PASTE_EVAL_AGAIN(x,y) x##y
  298. #define PASTE(x,y) PASTE_EVAL_AGAIN(x,y)
  299. #define STRINGIZE_A(x) STRINGIZE(PASTE(x,A))
  300. #define STRINGIZE_W(x) STRINGIZE(PASTE(x,W))
  301. */
  302. bool fReturnNewlinesInComments = false; /* untested */
  303. const char szOpenNamedObject[] = "Open Named Object";
  304. const char szCreateNamedObject[] = "Create Named Object";
  305. const char szRegistryWrite[] = "Registry Write";
  306. const char szFileWrite[] = "File Write";
  307. const char szRegOpenNotUnderstood[] = "RegOpen parameters not understood";
  308. const char szRegisteryRead[] = "Registry Read";
  309. const char szCOM1[] = "CoRegisterClassObject";
  310. const char szCOM2[] = "CoRegisterPSClsid";
  311. const char szOpenFile[] = "File I/O";
  312. const char szLOpen[] = "File I/O"; // UNDONE look at the access parameter
  313. const char szStructuredStorage[] = "Structured Storage I/O"; // UNDONE look at the access parameter
  314. const char szQueryWindowClass[] = "Query Window Class Info";
  315. const char szCreateWindowClass[] = "Create Window Class";
  316. const char szAtom[] = "Atom stuff";
  317. const char szRegisterWindowMessage[] = "Register Window Message";
  318. const char szCreateFileNotUnderstood[] = "CreateFile parameters not understood";
  319. const char szCreateObjectNotUnderstood[] = "Create object parameters not understood";
  320. const char szSetEnvironmentVariable[] = "Set Environment Variable";
  321. const char szWriteEventLog[] = "Event Log Write";
  322. struct HazardousFunction
  323. {
  324. const char* api;
  325. const char* message;
  326. const char* (*function)(const CClass&);
  327. int apiLength;
  328. //__int64 pad;
  329. };
  330. HazardousFunction hazardousFunctions[] =
  331. {
  332. #define HAZARDOUS_FUNCTION_AW3(api, x, y) \
  333. /* if we evaluate again, we pick up the macros from windows.h .. */ \
  334. { # api, x, y }, \
  335. { # api "A", x, y }, \
  336. { # api "W", x, y }
  337. #define HAZARDOUS_FUNCTION_AW2(api, x) \
  338. /* if we evaluate again, we pick up the macros from windows.h .. */ \
  339. { # api, x }, \
  340. { # api "A", x }, \
  341. { # api "W", x }
  342. #define HAZARDOUS_FUNCTION2(api, x ) \
  343. /* if we evaluate again, we pick up the macros from windows.h .. */ \
  344. { # api, x } \
  345. #define HAZARDOUS_FUNCTION3(api, x, y ) \
  346. /* if we evaluate again, we pick up the macros from windows.h .. */ \
  347. { # api, x, y } \
  348. /*--------------------------------------------------------------------------
  349. registry
  350. --------------------------------------------------------------------------*/
  351. HAZARDOUS_FUNCTION_AW2(RegCreateKey, szRegistryWrite),
  352. HAZARDOUS_FUNCTION_AW2(RegCreateKeyEx, szRegistryWrite),
  353. HAZARDOUS_FUNCTION_AW2(RegOpenKey, szRegistryWrite),
  354. // UNDONE check the access parameter
  355. HAZARDOUS_FUNCTION_AW3(RegOpenKeyEx, NULL, CheckRegOpenEx),
  356. HAZARDOUS_FUNCTION3(RegOpenUserClassesRoot, NULL, CheckRegOpenEx),
  357. HAZARDOUS_FUNCTION3(RegOpenCurrentUser, NULL, CheckRegOpenEx),
  358. //HAZARDOUS_FUNCTION_AW2(RegOpenKeyEx, szRegistryWrite),
  359. //HAZARDOUS_FUNCTION2(RegOpenUserClassesRoot, szRegistryWrite),
  360. // These don't require opening a key, they are legacy Win16 APIs
  361. HAZARDOUS_FUNCTION_AW2(RegSetValue, szRegistryWrite),
  362. // These are caught by the RegCreateKey or RegOpenKey with particular access.
  363. //HAZARDOUS_FUNCTION(RegSetValueEx, szReistryWrite),
  364. // SHReg* in shlwapi.dll
  365. HAZARDOUS_FUNCTION_AW2(SHRegCreateUSKey, szRegistryWrite),
  366. HAZARDOUS_FUNCTION_AW2(SHRegDeleteEmptyUSKey, szRegistryWrite),
  367. HAZARDOUS_FUNCTION_AW2(SHRegDeleteUSValue, szRegistryWrite),
  368. //UNDONEHAZARDOUS_FUNCTION_AW3(SHRegOpenUSKey, NULL, CheckSHRegOpen),
  369. HAZARDOUS_FUNCTION_AW2(SHRegSetPath, szRegistryWrite),
  370. HAZARDOUS_FUNCTION_AW2(SHRegSetUSValue, szRegistryWrite),
  371. // should be caught by OpenKey
  372. //HAZARDOUS_FUNCTION_AW2(SHRegWriteUSValue, szRegistryWrite),
  373. HAZARDOUS_FUNCTION_AW2(SetEnvironmentVariable, szSetEnvironmentVariable),
  374. /*--------------------------------------------------------------------------
  375. file i/o, esp. writing
  376. --------------------------------------------------------------------------*/
  377. HAZARDOUS_FUNCTION_AW3(CreateFile, NULL, CheckCreateFile),
  378. // legacy Win16 APIs. UNDONE check the access parameter, but
  379. // really any uses of these should be changed to CreateFile
  380. HAZARDOUS_FUNCTION2(OpenFile, szOpenFile),
  381. HAZARDOUS_FUNCTION2(_lopen, szLOpen),
  382. //HAZARDOUS_FUNCTION(fopen, szFOpen, CheckFOpen),
  383. /*--------------------------------------------------------------------------
  384. monikers
  385. --------------------------------------------------------------------------*/
  386. /*--------------------------------------------------------------------------
  387. structured storage
  388. --------------------------------------------------------------------------*/
  389. // UNDONE check the access parameter
  390. HAZARDOUS_FUNCTION2(StgOpenStorage, szStructuredStorage),
  391. HAZARDOUS_FUNCTION2(StgOpenStorageEx, szStructuredStorage),
  392. HAZARDOUS_FUNCTION2(StgCreateDocfile, szStructuredStorage),
  393. HAZARDOUS_FUNCTION2(StgCreateStorageEx, szStructuredStorage),
  394. /*--------------------------------------------------------------------------
  395. .exe servers / COM
  396. --------------------------------------------------------------------------*/
  397. HAZARDOUS_FUNCTION2(CoRegisterClassObject, szCOM1),
  398. HAZARDOUS_FUNCTION2(CoRegisterPSClsid, szCOM2),
  399. /*--------------------------------------------------------------------------
  400. named kernel objects
  401. --------------------------------------------------------------------------*/
  402. // Create named or anonymous, anonymous is not a hazard
  403. HAZARDOUS_FUNCTION_AW3(CreateDesktop, NULL, CheckCreateObject),
  404. HAZARDOUS_FUNCTION_AW3(CreateEvent, NULL, CheckCreateObject),
  405. HAZARDOUS_FUNCTION_AW3(CreateFileMapping, NULL, CheckCreateObject),
  406. HAZARDOUS_FUNCTION_AW3(CreateJobObject, NULL, CheckCreateObject),
  407. HAZARDOUS_FUNCTION_AW3(CreateMutex, NULL, CheckCreateObject),
  408. HAZARDOUS_FUNCTION_AW2(CreateMailslot, szOpenNamedObject), // never anonymous
  409. HAZARDOUS_FUNCTION_AW2(CreateNamedPipe, szOpenNamedObject), // never anonymous
  410. HAZARDOUS_FUNCTION_AW3(CreateSemaphore, NULL, CheckCreateObject),
  411. HAZARDOUS_FUNCTION_AW3(CreateWaitableTimer, NULL, CheckCreateObject),
  412. HAZARDOUS_FUNCTION_AW3(CreateWindowStation, NULL, CheckCreateObject),
  413. // open by name
  414. HAZARDOUS_FUNCTION_AW2(OpenDesktop, szOpenNamedObject),
  415. HAZARDOUS_FUNCTION_AW2(OpenEvent, szOpenNamedObject),
  416. HAZARDOUS_FUNCTION_AW2(OpenFileMapping, szOpenNamedObject),
  417. HAZARDOUS_FUNCTION_AW2(OpenJobObject, szOpenNamedObject),
  418. HAZARDOUS_FUNCTION_AW2(OpenMutex, szOpenNamedObject),
  419. HAZARDOUS_FUNCTION_AW2(CallNamedPipe, szOpenNamedObject),
  420. HAZARDOUS_FUNCTION_AW2(OpenSemaphore, szOpenNamedObject),
  421. HAZARDOUS_FUNCTION_AW2(OpenWaitableTimer, szOpenNamedObject),
  422. HAZARDOUS_FUNCTION_AW2(OpenWindowStation, szOpenNamedObject),
  423. /*
  424. EnumProcesses?
  425. Toolhelp
  426. */
  427. /*--------------------------------------------------------------------------
  428. window classes
  429. --------------------------------------------------------------------------*/
  430. // Fusion should handle these automagically.
  431. // these two take a class name as a parameter
  432. // this still produces many false positives..
  433. //HAZARDOUS_FUNCTION_AW2(WNDCLASS, szQueryWindowClass),
  434. //HAZARDOUS_FUNCTION_AW2(WNDCLASSEX, szQueryWindowClass),
  435. //HAZARDOUS_FUNCTION_AW2(GetClassInfoEx, szQueryWindowClass),
  436. // Fusion should handle these automagically.
  437. // this returns a class name
  438. // HAZARDOUS_FUNCTION_AW2(GetClassName, szQueryWindowClass),
  439. // Fusion should handle these automagically.
  440. // this creates classes
  441. //HAZARDOUS_FUNCTION_AW2(RegisterClass, szCreateWindowClass),
  442. //HAZARDOUS_FUNCTION_AW2(RegisterClassEx, szCreateWindowClass),
  443. /*--------------------------------------------------------------------------
  444. window messages
  445. --------------------------------------------------------------------------*/
  446. // We aren't convinced this is a problem.
  447. //HAZARDOUS_FUNCTION_AW2(RegisterWindowMessage, szRegisterWindowMessage),
  448. /*--------------------------------------------------------------------------
  449. atoms
  450. --------------------------------------------------------------------------*/
  451. HAZARDOUS_FUNCTION_AW2(AddAtom, szAtom),
  452. HAZARDOUS_FUNCTION_AW2(FindAtom, szAtom),
  453. HAZARDOUS_FUNCTION_AW2(GlobalAddAtom, szAtom),
  454. HAZARDOUS_FUNCTION_AW2(GlobalFindAtom, szAtom),
  455. /*
  456. InitAtomTable,
  457. DeleteAtom,
  458. GetAtomName,
  459. GlobalDeleteAtom,
  460. GlobalGetAtomName
  461. */
  462. /*--------------------------------------------------------------------------
  463. DDE?
  464. clipboard?
  465. --------------------------------------------------------------------------*/
  466. /*--------------------------------------------------------------------------
  467. Ole data transfer
  468. --------------------------------------------------------------------------*/
  469. HAZARDOUS_FUNCTION2(RegisterMediaTypeClass, szRegistryWrite),
  470. HAZARDOUS_FUNCTION2(RegisterMediaTypes, szRegistryWrite),
  471. HAZARDOUS_FUNCTION2(OleUICanConvertOrActivatfeAs, szRegisteryRead),
  472. HAZARDOUS_FUNCTION2(OleRegEnumFormatEtc, szRegisteryRead),
  473. HAZARDOUS_FUNCTION2(OleRegEnumVerbs, szRegisteryRead),
  474. /*--------------------------------------------------------------------------
  475. NT event log
  476. --------------------------------------------------------------------------*/
  477. HAZARDOUS_FUNCTION_AW2(RegisterEventSource, szRegistryWrite),
  478. HAZARDOUS_FUNCTION_AW2(ClearEventLog, szWriteEventLog),
  479. HAZARDOUS_FUNCTION_AW2(ReportEvent, szWriteEventLog),
  480. /*--------------------------------------------------------------------------
  481. UNDONE think about these
  482. HAZARDOUS_FUNCTION_AW2(CreateEnhMetaFile, szWriteGdiMetaFile),
  483. HAZARDOUS_FUNCTION_AW2(DeleteEnhMetaFile, szWriteGdiMetaFile),
  484. HAZARDOUS_FUNCTION_AW2(DeleteMetaFile, szWriteGdiMetaFile),
  485. HAZARDOUS_FUNCTION_AW2(CreateMetaFile, szWriteGdiMetaFile),
  486. HAZARDOUS_FUNCTION_AW2(DeleteFile, szWriteFileSystem),
  487. HAZARDOUS_FUNCTION_AW2(MoveFile, szWriteFileSystem),
  488. HAZARDOUS_FUNCTION_AW2(RemoveDirectory, szWriteFileSystem),
  489. HAZARDOUS_FUNCTION_AW2(ReplaceFile, szWriteFileSystem),
  490. RegDelete* (handled by RegOpen)
  491. SHReg* (?handled by open)
  492. CreateService OpenSCManager (not done)
  493. SetWindowsHook SetWindowsHookEx (not done)
  494. --------------------------------------------------------------------------*/
  495. };
  496. int __cdecl CompareHazardousFunction(const HazardousFunction* x, const HazardousFunction* y)
  497. {
  498. int i = 0;
  499. int minlength = 0;
  500. /* one of the strings is not nul terminated */
  501. if (x->apiLength == y->apiLength)
  502. {
  503. i = strncmp(x->api, y->api, x->apiLength);
  504. return i;
  505. }
  506. minlength = x->apiLength < y->apiLength ? x->apiLength : y->apiLength;
  507. i = strncmp(x->api, y->api, minlength);
  508. if (i != 0)
  509. return i;
  510. return (minlength == x->apiLength) ? -1 : +1;
  511. }
  512. bool leadingTwoCharsAs14Bits[1U<<14];
  513. void CClass::UngetCharacter2(int ch)
  514. {
  515. /* if you m_rgchUnget a newline, line numbers will get messed up
  516. we only m_rgchUnget forward slashes
  517. */
  518. m_rgchUnget[m_nUnget++] = static_cast<char>(ch);
  519. }
  520. int CClass::GetCharacter2()
  521. {
  522. int ch;
  523. m_line = m_nextLine; /* UNDONE clean this hack up */
  524. if (m_nUnget)
  525. {
  526. return m_rgchUnget[--m_nUnget];
  527. }
  528. /* undone break the nul termination dependency.. */
  529. if (m_str == m_end)
  530. return 0;
  531. ch = *m_str;
  532. m_str += 1;
  533. switch (ch)
  534. {
  535. /* it is for line continuation that this function is really needed.. */
  536. case '\\': /* line continuation not implemented */
  537. case '?': /* trigraphs not implemented */
  538. default:
  539. break;
  540. case 0xc: /* formfeed, control L, very common in NT source */
  541. ch = '\n';
  542. break;
  543. case '\t':
  544. ch = ' ';
  545. break;
  546. case '\r':
  547. ch = '\n';
  548. /* skip \n after \r */
  549. if (*m_str == '\n')
  550. m_str += 1;
  551. /* fall through */
  552. case '\n':
  553. m_nextLine.m_start = m_str;
  554. m_nextLine.m_number += 1;
  555. //if (m_nextLine.m_number == 92)
  556. //{
  557. // BreakPoint();
  558. //}
  559. break;
  560. }
  561. return ch;
  562. }
  563. int CClass::GetCharacter()
  564. {
  565. int ch;
  566. if (m_fInComment)
  567. goto Lm_fInComment;
  568. m_fInComment = false;
  569. ch = GetCharacter2();
  570. switch (ch)
  571. {
  572. default:
  573. goto Lret;
  574. case '/':
  575. ch = GetCharacter2();
  576. switch (ch)
  577. {
  578. default:
  579. UngetCharacter2(ch);
  580. ch = '/';
  581. goto Lret;
  582. case '/':
  583. while ((ch = GetCharacter2())
  584. && !IsVerticalSpace(ch))
  585. {
  586. /* nothing */
  587. }
  588. goto Lret; /* return the \n or 0*/
  589. case '*':
  590. Lm_fInComment:
  591. L2:
  592. ch = GetCharacter2();
  593. switch (ch)
  594. {
  595. case '\n':
  596. if (!fReturnNewlinesInComments)
  597. goto L2;
  598. m_fInComment = true;
  599. goto Lret;
  600. default:
  601. goto L2;
  602. case 0:
  603. /* unclosed comment at end of file, just return end */
  604. printf("unclosed comment\n");
  605. goto Lret;
  606. case '*':
  607. L1:
  608. ch = GetCharacter2();
  609. switch (ch)
  610. {
  611. default:
  612. goto L2;
  613. case 0:
  614. /* unclosed comment at end of file, just return end */
  615. printf("unclosed comment\n");
  616. goto Lret;
  617. case '/':
  618. ch = ' ';
  619. goto Lret;
  620. case '*':
  621. goto L1;
  622. }
  623. }
  624. }
  625. }
  626. Lret:
  627. return ch;
  628. }
  629. CClass::CClass(const CClass& that)
  630. {
  631. // yucky laziness
  632. memcpy(this, &that, sizeof(*this));
  633. // the copy must not outlive the original!
  634. // or we could DuplicateHandle the handles and make a new mapping..
  635. m_begin = that.m_str;
  636. m_file.Detach();
  637. m_fileMapping.Detach();
  638. m_view.Detach();
  639. }
  640. CClass::CClass()
  641. {
  642. // yucky laziness
  643. memset(this, 0, sizeof(*this));
  644. // m_fPrintContext = false;
  645. // m_fPrintFullStatement = true;
  646. m_fPrintCarets = false;
  647. m_fPoundIsPreprocessor = true;
  648. m_file.Detach();
  649. m_fileMapping.Detach();
  650. m_view.Detach();
  651. }
  652. ETokenType CClass::GetToken()
  653. {
  654. int i = 0;
  655. int ch = 0;
  656. unsigned twoCharsAs14Bits = 0;
  657. char ch2[3] = {0,0,0};
  658. HazardousFunction lookingForHazardousFunction;
  659. HazardousFunction* foundHazardousFunction;
  660. L1:
  661. ch = GetCharacter();
  662. switch (ch)
  663. {
  664. default:
  665. m_fPoundIsPreprocessor = false;
  666. break;
  667. CASE_VERTICAL_SPACE:
  668. m_fPoundIsPreprocessor = true;
  669. goto L1;
  670. case '#':
  671. break;
  672. CASE_HORIZONTAL_SPACE:
  673. goto L1;
  674. }
  675. switch (ch)
  676. {
  677. default:
  678. BreakPoint();
  679. printf("\n%s(%d): unhandled character %c, stopping processing this file\n", m_fullPath, m_line.m_number, ch);
  680. case 0:
  681. return INT_TO_ENUM_CAST(ETokenType)(0);
  682. break;
  683. /* FUTURE, we pick these up as bogus seperate tokens instead of doing
  684. line continuation */
  685. case '\\':
  686. return (m_eTokenType = INT_TO_ENUM_CAST(ETokenType)(ch));
  687. /* one character tokens */
  688. case '{': /* 0x7b to turn off editor interaction.. */
  689. case '}': /* 0x7D to turn off editor interaction.. */
  690. case '?': /* we don't handle trigraphs */
  691. case '[':
  692. case ']':
  693. case '(':
  694. case ',':
  695. case ')':
  696. case ';':
  697. return (m_eTokenType = INT_TO_ENUM_CAST(ETokenType)(ch));
  698. /* one two or three character tokens */
  699. case '.': /* . .* */
  700. case '<': /* < << <<= */
  701. case '>': /* > >> >>= */
  702. case '+': /* + ++ += */
  703. case '-': /* - -- -= -> ->* */
  704. case '*': /* * *= */ /* and ** in C9x */
  705. case '/': /* / /= */
  706. case '%': /* % %= */
  707. case '^': /* ^ ^= */
  708. case '&': /* & && &= */
  709. case '|': /* | || |= */
  710. case '~': /* ~ ~= */
  711. case '!': /* ! != */
  712. case ':': /* : :: */
  713. case '=': /* = == */
  714. /* just lie and return them one char at a time */
  715. return (m_eTokenType = INT_TO_ENUM_CAST(ETokenType)(ch));
  716. case '#':
  717. /* not valid in general, only if m_fPoundIsPreprocessor or actually in a #define,
  718. but we don't care */
  719. if (!m_fPoundIsPreprocessor)
  720. return (m_eTokenType = INT_TO_ENUM_CAST(ETokenType)(ch));
  721. /* lack of backslash line continuation makes the most difference here */
  722. while ((ch = GetCharacter()) && !IsVerticalSpace(ch))
  723. {
  724. /* nothing */
  725. }
  726. return (m_eTokenType = eTokenTypePreprocessorDirective);
  727. case '\'':
  728. /* NT uses multi char char constants..
  729. call GetCharacter2 instead of GetCharacter so that
  730. comments in char constants are not treated as comments */
  731. while ((ch = GetCharacter2()) && ch != '\'')
  732. {
  733. /* notice the bogosity, escapes are multiple characters,
  734. but \' is not, and that's all we care about skipping correctly */
  735. if (ch == '\\')
  736. GetCharacter2();
  737. }
  738. return (m_eTokenType = eTokenTypeCharConstant);
  739. case '\"':
  740. /* call GetCharacter2 instead of GetCharacter so that
  741. comments in string constants are not treated as comments */
  742. while ((ch = GetCharacter2()) && ch != '\"')
  743. {
  744. /* notice the bogosity, escapes are multiple characters,
  745. but \" is not, and that's all we care about skipping correctly */
  746. if (ch == '\\')
  747. GetCharacter2();
  748. }
  749. return (m_eTokenType = eTokenTypeStringConstant);
  750. CASE_09:
  751. /* integer or floating point, including hex,
  752. we ignore some invalid forms */
  753. m_str += SequenceLengthOfSpanIncluding(m_str, m_end, digitsAll, digitsAll+NUMBER_OF(digitsAll)-1);
  754. return (m_eTokenType = eTokenTypeNumber);
  755. case '$': /* non standard, used by NT */
  756. CASE_AZ:
  757. CASE_az:
  758. case '_':
  759. /* notice, keywords like if/else/while/class are just
  760. returned as identifiers, that is sufficient for now
  761. */
  762. i = SequenceLengthOfSpanIncluding(m_str, m_end, identifierChars, identifierChars+NUMBER_OF(identifierChars)-1);
  763. twoCharsAs14Bits = StringLeadingTwoCharsTo14Bits(m_str - 1);
  764. if (!leadingTwoCharsAs14Bits[twoCharsAs14Bits])
  765. goto LtokenIdentifier;
  766. lookingForHazardousFunction.api = m_str - 1;
  767. lookingForHazardousFunction.apiLength = i + 1;
  768. foundHazardousFunction = REINTERPRET_CAST(HazardousFunction*)(bsearch(&lookingForHazardousFunction, hazardousFunctions, NUMBER_OF(hazardousFunctions), sizeof(hazardousFunctions[0]), (BsearchFunction)CompareHazardousFunction));
  769. if (!foundHazardousFunction)
  770. goto LtokenIdentifier;
  771. m_tokenText = m_str - 1;
  772. m_tokenLength = i + 1;
  773. m_str += i;
  774. m_hazardousFunction = foundHazardousFunction;
  775. return (m_eTokenType = eTokenTypeHazardousFunction);
  776. LtokenIdentifier:
  777. m_tokenText = m_str - 1;
  778. m_tokenLength = i + 1;
  779. m_str += i;
  780. return (m_eTokenType = eTokenTypeIdentifier);
  781. }
  782. }
  783. /*
  784. This has the desired affect of skipping comments.
  785. FUTURE but it doesn't skip preprocessor directives because they
  786. are handled within ScannerGetToken.
  787. This is a bug if your code looks like
  788. CreateFile(GENERIC_READ|
  789. #include "foo.h"
  790. GENERIC_WRITE,
  791. ...
  792. );
  793. returning an int length here is problematic because of \r\n conversion..yuck.
  794. */
  795. const char* CClass::SpanEnd(const char* set) const
  796. {
  797. CClass s(*this);
  798. int ch;
  799. while ((ch = s.GetCharacter())
  800. && strchr(set, ch))
  801. {
  802. /* nothing */
  803. }
  804. return s.m_str;
  805. }
  806. bool CClass::ScanToCharacter(int ch)
  807. {
  808. int ch2;
  809. if (!ch)
  810. {
  811. return false;
  812. }
  813. while ((ch2 = GetCharacter()) && ch2 != ch)
  814. {
  815. /* nothing */
  816. }
  817. return (ch2 == ch);
  818. }
  819. /* This has the desired advantage over strstr of
  820. - skip comments
  821. - not depend on nul termination
  822. */
  823. bool CClass::FindString(const char* str) const
  824. {
  825. CClass s(*this);
  826. int ch;
  827. int ch0 = *str++;
  828. while (s.ScanToCharacter(ch0))
  829. {
  830. const char* str2 = str;
  831. CClass t(s);
  832. while (*str2 && (ch = t.GetCharacter()) && ch == *str2)
  833. {
  834. ++str2;
  835. }
  836. if (!*str2)
  837. return true;
  838. }
  839. return false;
  840. }
  841. unsigned short __stdcall StringLeadingTwoCharsTo14Bits(const char* s)
  842. {
  843. unsigned short result = s[0];
  844. result <<= 7;
  845. result |= result ? s[1] : 0;
  846. return result;
  847. }
  848. void __stdcall InitTables()
  849. {
  850. int i;
  851. for (i = 0 ; i != NUMBER_OF(hazardousFunctions) ; ++i)
  852. {
  853. leadingTwoCharsAs14Bits[StringLeadingTwoCharsTo14Bits(hazardousFunctions[i].api)] = true;
  854. hazardousFunctions[i].apiLength = strlen(hazardousFunctions[i].api);
  855. }
  856. qsort(&hazardousFunctions, i, sizeof(hazardousFunctions[0]), FUNCTION_POINTER_CAST(QsortFunction)(CompareHazardousFunction));
  857. }
  858. const char* CClass::ScanToFirstParameter()
  859. {
  860. // scan to left paren, but if we see right paren or semi first, return null
  861. // FUTURE count parens..
  862. int ch;
  863. while (ch = GetCharacter())
  864. {
  865. switch (ch)
  866. {
  867. default:
  868. break;
  869. case '(':
  870. return m_str;
  871. case ';':
  872. case ')':
  873. return 0;
  874. }
  875. }
  876. return 0;
  877. }
  878. const char* CClass::ScanToLastParameter()
  879. {
  880. const char* ret = 0;
  881. if (ScanToFirstParameter())
  882. {
  883. ret = m_str;
  884. while (ScanToNextParameter())
  885. {
  886. ret = m_str;
  887. }
  888. }
  889. return ret;
  890. }
  891. const char* CClass::ScanToNthParameter(int n)
  892. {
  893. const char* ret = 0;
  894. if (!(ret = ScanToFirstParameter()))
  895. return ret;
  896. while (n-- > 0 && (ret = ScanToNextParameter()))
  897. {
  898. /* nothing */
  899. }
  900. return ret;
  901. }
  902. int CClass::CountParameters() const
  903. {
  904. CClass s(*this);
  905. int result = 0;
  906. if (!s.ScanToFirstParameter())
  907. return result;
  908. ++result;
  909. while (s.ScanToNextParameter())
  910. {
  911. ++result;
  912. }
  913. return result;
  914. }
  915. const char* CClass::ScanToNextParameter()
  916. {
  917. int parenlevel = 1;
  918. while (true)
  919. {
  920. int ch = GetCharacter();
  921. switch (ch)
  922. {
  923. default:
  924. break;
  925. case 0:
  926. printf("end of file scanning for next parameter\n");
  927. /* worst case macro confusion, we go to end of file */
  928. return 0;
  929. case '(':
  930. ++parenlevel;
  931. break;
  932. case ')':
  933. if (--parenlevel == 0)
  934. { /* no next parameter */
  935. return 0;
  936. }
  937. break;
  938. case ',':
  939. if (parenlevel == 1)
  940. {
  941. return m_str;
  942. }
  943. break;
  944. case '#':
  945. /* bad case macro confusion, go to end of statement */
  946. printf("# while scanning for parameters\n");
  947. return 0;
  948. case ';':
  949. /* bad case macro confusion, go to end of statement */
  950. printf("end of statement (;) while scanning for parameters\n");
  951. return 0;
  952. }
  953. }
  954. }
  955. const char* __stdcall CheckRegOpenCommon(CClass& subScanner, int argcRegSam)
  956. {
  957. const char* regsam = subScanner.ScanToNthParameter(argcRegSam);
  958. const char* next = regsam ? subScanner.ScanToNextParameter() : 0;
  959. // allow for it to be last parameter, which it is sometimes
  960. if (!next)
  961. next = subScanner.m_str;
  962. if (!regsam || !next)
  963. {
  964. return szRegOpenNotUnderstood;
  965. }
  966. subScanner.m_str = regsam;
  967. subScanner.m_end = next;
  968. const char* endOfValidSam = subScanner.SpanEnd(UPPER_LETTERS "_|,()0" SPACE);
  969. if (endOfValidSam != next && endOfValidSam != next+1)
  970. {
  971. return szRegOpenNotUnderstood;
  972. }
  973. if (
  974. subScanner.FindString("ALL") // both "all" and "maximum_allowed"
  975. || subScanner.FindString("SET")
  976. || subScanner.FindString("WRITE")
  977. || subScanner.FindString("CREATE")
  978. )
  979. {
  980. return szRegistryWrite;
  981. }
  982. // not a problem, registry only opened for read
  983. return 0;
  984. }
  985. const char* __stdcall CheckSHRegOpen(const CClass& scanner)
  986. {
  987. CClass subScanner(scanner);
  988. return CheckRegOpenCommon(subScanner, 1);
  989. }
  990. const char* __stdcall CheckRegOpenEx(const CClass& scanner)
  991. /*
  992. this function is used for all of RegOpenKeyEx, RegOpenCurrentUser, RegOpenUserClassesRoot,
  993. which is why it uses argc-2 instead of a particular parameter from the start.
  994. */
  995. {
  996. CClass subScanner(scanner);
  997. const int argc = subScanner.CountParameters();
  998. return CheckRegOpenCommon(subScanner, argc - 2);
  999. }
  1000. const char* __stdcall CheckCreateObject(const CClass& scanner)
  1001. {
  1002. CClass subScanner(scanner);
  1003. const char* name;
  1004. name = subScanner.ScanToLastParameter();
  1005. if (!name)
  1006. {
  1007. return szCreateObjectNotUnderstood;
  1008. }
  1009. subScanner.m_str = name;
  1010. int ch;
  1011. while (
  1012. (ch = subScanner.GetCharacter())
  1013. && IsSpace(ch)
  1014. )
  1015. {
  1016. }
  1017. name = subScanner.m_str - 1;
  1018. if (!name)
  1019. {
  1020. return szCreateObjectNotUnderstood;
  1021. }
  1022. if (
  1023. strncmp(name, "0", 1) == 0
  1024. || strncmp(name, "NULL", 4) == 0)
  1025. {
  1026. // not a sharing hazard
  1027. return 0;
  1028. }
  1029. return szOpenNamedObject;
  1030. }
  1031. const char* __stdcall CheckCreateFile(const CClass& scanner)
  1032. {
  1033. CClass subScanner(scanner);
  1034. const char* access = subScanner.ScanToNthParameter(1);
  1035. const char* share = access ? subScanner.ScanToNextParameter() : 0;
  1036. const char* endOfValidAccess = 0;
  1037. if (!access || !share)
  1038. {
  1039. return szCreateFileNotUnderstood;
  1040. }
  1041. subScanner.m_str = access;
  1042. subScanner.m_end = share;
  1043. endOfValidAccess = subScanner.SpanEnd(UPPER_LETTERS "_|,()0" SPACE);
  1044. if (endOfValidAccess != share)
  1045. {
  1046. return szCreateFileNotUnderstood;
  1047. }
  1048. /* GENERIC_WRITE WRITE_DAC WRITE_OWNER STANDARD_RIGHTS_WRITE
  1049. STANDARD_RIGHTS_ALL SPECIFIC_RIGHTS_ALL MAXIMUM_ALLOWED GENERIC_ALL
  1050. */
  1051. if (
  1052. subScanner.FindString("WRITE")
  1053. || subScanner.FindString("ALL")
  1054. || subScanner.FindString("0")
  1055. )
  1056. {
  1057. return szFileWrite;
  1058. }
  1059. return 0;
  1060. }
  1061. void CClass::Warn(const char* message) const
  1062. {
  1063. if (message && *message)
  1064. {
  1065. //PrintOpenComment();
  1066. printf("%s(%d): %s\n", m_fullPath, m_line.m_number, message);
  1067. //PrintCloseComment();
  1068. }
  1069. else
  1070. {
  1071. PrintSeperator();
  1072. }
  1073. PrintCode();
  1074. }
  1075. bool CClass::OpenFile(const char* name)
  1076. {
  1077. if (FAILED(m_file.HrCreate(name, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING)))
  1078. return false;
  1079. if (FAILED(m_fileMapping.HrCreate(m_file, PAGE_READONLY)))
  1080. return false;
  1081. if (FAILED(m_view.HrCreate(m_fileMapping, FILE_MAP_READ)))
  1082. return false;
  1083. m_str = reinterpret_cast<const char*>(static_cast<const void*>(m_view));
  1084. m_end = m_str + m_file.GetSize();
  1085. m_nextLine.m_start = m_line.m_start = m_str;
  1086. m_nextLine.m_number += 1;
  1087. return true;
  1088. }
  1089. void CClass::RecordStatement(int ch)
  1090. {
  1091. if (ch == ';' || ch == '{' || ch == eTokenTypePreprocessorDirective /*|| ch == '}'*/)
  1092. {
  1093. if (!m_statementStart)
  1094. {
  1095. m_statementStart = m_str;
  1096. }
  1097. else
  1098. {
  1099. CStatement statement = { m_statementStart, m_str};
  1100. m_statements[m_istatement % NUMBER_OF(m_statements)] = statement;
  1101. ++m_istatement;
  1102. m_statementStart = m_str;
  1103. }
  1104. }
  1105. }
  1106. /* do this before iterating over them to print them, this makes the iteration
  1107. interface simple */
  1108. void CClass::OrderStatements() const /* mutable */
  1109. {
  1110. const int N = NUMBER_OF(m_statements);
  1111. CStatement temp[N];
  1112. std::copy(m_statements, m_statements + N, temp);
  1113. for (int i = 0 ; i != N ; ++i)
  1114. {
  1115. m_statements[i] = temp[(m_istatement + i) % N];
  1116. }
  1117. m_istatement = 0;
  1118. }
  1119. void __stdcall PrintOpenComment()
  1120. {
  1121. const int N = 76;
  1122. static char str[N];
  1123. if (!str[0])
  1124. {
  1125. std::fill(str + 1, str + N - 1, '-');
  1126. str[2] = '*';
  1127. str[1] = '/';
  1128. str[0] = '\n';
  1129. }
  1130. fputs(str, stdout);
  1131. }
  1132. void __stdcall PrintCloseComment()
  1133. {
  1134. const int N = 76;
  1135. static char str[N];
  1136. if (!str[0])
  1137. {
  1138. std::fill(str + 1, str + N - 1, '-');
  1139. str[N-3] = '*';
  1140. str[N-2] = '/';
  1141. str[0] = '\n';
  1142. }
  1143. fputs(str, stdout);
  1144. }
  1145. void __stdcall PrintSeperator()
  1146. {
  1147. const int N = 76;
  1148. static char str[N];
  1149. if (!str[0])
  1150. {
  1151. std::fill(str + 1, str + N - 1, '-');
  1152. str[0] = '\n';
  1153. }
  1154. fputs(str, stdout);
  1155. }
  1156. void __stdcall TrimSpaces(const char** begin, const char** end)
  1157. {
  1158. while (*begin != *end && IsSpace(**begin))
  1159. {
  1160. ++*begin;
  1161. }
  1162. while (*begin != *end && IsSpace(*(*end - 1)))
  1163. {
  1164. --*end;
  1165. }
  1166. }
  1167. void __stdcall PrintString(const char* begin, const char* end)
  1168. {
  1169. if (begin && end > begin)
  1170. {
  1171. int length = end - begin;
  1172. printf("%.*s", length, begin);
  1173. }
  1174. }
  1175. const char* __stdcall RemoveLeadingSpace(const char* begin, const char* end)
  1176. {
  1177. if (begin != end && IsSpace(*begin))
  1178. {
  1179. while (begin != end && IsSpace(*begin))
  1180. {
  1181. ++begin;
  1182. }
  1183. }
  1184. return begin;
  1185. }
  1186. const char* __stdcall RemoveLeadingVerticalSpace(const char* begin, const char* end)
  1187. {
  1188. if (begin != end && IsVerticalSpace(*begin))
  1189. {
  1190. while (begin != end && IsVerticalSpace(*begin))
  1191. {
  1192. ++begin;
  1193. }
  1194. }
  1195. return begin;
  1196. }
  1197. const char* __stdcall OneLeadingVerticalSpace(const char* begin, const char* end)
  1198. {
  1199. if (begin != end && IsVerticalSpace(*begin))
  1200. {
  1201. while (begin != end && IsVerticalSpace(*begin))
  1202. {
  1203. ++begin;
  1204. }
  1205. --begin;
  1206. }
  1207. return begin;
  1208. }
  1209. const char* __stdcall RemoveTrailingSpace(const char* begin, const char* end)
  1210. {
  1211. if (begin != end && IsSpace(*(end-1)))
  1212. {
  1213. while (begin != end && IsSpace(*(end-1)))
  1214. {
  1215. --end;
  1216. }
  1217. }
  1218. return end;
  1219. }
  1220. const char* __stdcall RemoveTrailingVerticalSpace(const char* begin, const char* end)
  1221. {
  1222. if (begin != end && IsVerticalSpace(*(end-1)))
  1223. {
  1224. while (begin != end && IsVerticalSpace(*(end-1)))
  1225. {
  1226. --end;
  1227. }
  1228. }
  1229. return end;
  1230. }
  1231. const char* __stdcall OneTrailingVerticalSpace(const char* begin, const char* end)
  1232. {
  1233. if (begin != end && IsVerticalSpace(*(end-1)))
  1234. {
  1235. while (begin != end && IsVerticalSpace(*(end-1)))
  1236. {
  1237. --end;
  1238. }
  1239. ++end;
  1240. }
  1241. return end;
  1242. }
  1243. void CClass::PrintCode() const
  1244. {
  1245. // this function is messy wrt when newlines are printed
  1246. //PrintSeperator();
  1247. OrderStatements();
  1248. int i;
  1249. if (g_nPrintContextStatements)
  1250. {
  1251. const char* previousStatementsEnd = 0;
  1252. for (i = NUMBER_OF(m_statements) - g_nPrintContextStatements ; i != NUMBER_OF(m_statements) ; i++)
  1253. {
  1254. if (m_statements[i].begin && m_statements[i].end)
  1255. {
  1256. previousStatementsEnd = m_statements[i].end;
  1257. // for the first iteration, limit ourselves to one newline
  1258. if (i == NUMBER_OF(m_statements) - g_nPrintContextStatements)
  1259. {
  1260. m_statements[i].begin = RemoveLeadingVerticalSpace(m_statements[i].begin, m_statements[i].end);
  1261. }
  1262. PrintString(m_statements[i].begin, m_statements[i].end);
  1263. }
  1264. }
  1265. if (previousStatementsEnd)
  1266. {
  1267. PrintString(previousStatementsEnd, m_line.m_start);
  1268. }
  1269. }
  1270. const char* newlineChar = SequenceLinearFindValue(m_line.m_start, m_end, '\n');
  1271. const char* returnChar = SequenceLinearFindValue(m_line.m_start, m_end, '\r');
  1272. const char* endOfLine = std::min(newlineChar, returnChar);
  1273. int outputLineOffset = 0;
  1274. int lineLength = endOfLine - m_line.m_start;
  1275. if (g_fPrintLine)
  1276. {
  1277. printf("%.*s\n", lineLength, m_line.m_start);
  1278. }
  1279. // underline the offending hazardous function with carets
  1280. if (g_nPrintContextStatements)
  1281. {
  1282. // skip the part of the line preceding the hazardous functon,
  1283. // print tabs where it has tabs
  1284. for (i = 0 ; i < m_str - m_line.m_start - m_hazardousFunction->apiLength ; ++i)
  1285. {
  1286. fputs((m_line.m_start[i] != '\t') ? " " : "\t"/*" "*/, stdout);
  1287. }
  1288. // underline the function with carets
  1289. for (i = 0 ; i < m_hazardousFunction->apiLength ; ++i)
  1290. putchar('^');
  1291. putchar('\n');
  1292. }
  1293. // find the approximate end of statement
  1294. const char* statementSemi = SequenceLinearFindValue(m_line.m_start, m_end, ';');
  1295. const char* statementBrace = SequenceLinearFindValue(m_line.m_start, m_end, '{'); // }
  1296. const char* statementPound = SequenceLinearFindValue(m_line.m_start, m_end, '#');
  1297. // statements don't really end in a pound, but this helps terminate output
  1298. // in some cases
  1299. const char* statementEnd = RemoveTrailingSpace(m_line.m_start, std::min(std::min(statementSemi, statementBrace), statementPound));
  1300. if (g_fPrintFullStatement)
  1301. {
  1302. if (statementEnd > endOfLine)
  1303. {
  1304. const char* statementBegin = RemoveLeadingVerticalSpace(endOfLine, statementEnd);
  1305. if (*statementEnd == ';')
  1306. {
  1307. ++statementEnd;
  1308. }
  1309. PrintString(statementBegin, statementEnd);
  1310. if (!g_nPrintContextStatements)
  1311. {
  1312. putchar('\n');
  1313. }
  1314. }
  1315. else
  1316. {
  1317. if (*statementEnd == ';')
  1318. {
  1319. ++statementEnd;
  1320. }
  1321. }
  1322. }
  1323. // print more statements after it
  1324. if (g_nPrintContextStatements)
  1325. {
  1326. for (i = 0 ; i != g_nPrintContextStatements ; ++i)
  1327. {
  1328. // and then a few more
  1329. const char* statement2 = SequenceLinearFindValue(statementEnd, m_end, ';');
  1330. if (i == 0)
  1331. {
  1332. statementEnd = RemoveLeadingVerticalSpace(statementEnd, statement2);
  1333. }
  1334. if (i == g_nPrintContextStatements-1)
  1335. {
  1336. statement2 = RemoveTrailingSpace(statementEnd, statement2);
  1337. PrintString(statementEnd, statement2);
  1338. putchar(';');
  1339. putchar('\n');
  1340. }
  1341. else
  1342. {
  1343. PrintString(statementEnd, statement2);
  1344. putchar(';');
  1345. }
  1346. statementEnd = statement2 + (statement2 != m_end);
  1347. }
  1348. }
  1349. }
  1350. int CSharingHazardCheck::ProcessFile(const std::string& name)
  1351. {
  1352. // test argv processing
  1353. // std::cout << name << std::endl;
  1354. // return;
  1355. int total = 0;
  1356. CClass scanner;
  1357. ETokenType m_eTokenType = INT_TO_ENUM_CAST(ETokenType)(0);
  1358. scanner.m_fullPath[0] = 0;
  1359. if (!GetFullPathName(name.c_str(), NUMBER_OF(scanner.m_fullPath), scanner.m_fullPath, NULL))
  1360. strcpy(scanner.m_fullPath, name.c_str());
  1361. if (!scanner.OpenFile(scanner.m_fullPath))
  1362. return total;
  1363. scanner.RecordStatement(';');
  1364. // we "parse" token :: token, to avoid these false positives
  1365. int idColonColonState = 0;
  1366. while (m_eTokenType = scanner.GetToken())
  1367. {
  1368. scanner.RecordStatement(m_eTokenType);
  1369. switch (m_eTokenType)
  1370. {
  1371. default:
  1372. idColonColonState = 0;
  1373. break;
  1374. case eTokenTypeIdentifier:
  1375. idColonColonState = 1;
  1376. break;
  1377. case ':':
  1378. switch (idColonColonState)
  1379. {
  1380. case 1:
  1381. idColonColonState = 2;
  1382. break;
  1383. case 2:
  1384. // skip a token
  1385. // idColonColonState = 3;
  1386. scanner.GetToken();
  1387. // idColonColonState = 0;
  1388. // break;
  1389. case 0:
  1390. //case 3:
  1391. idColonColonState = 0;
  1392. break;
  1393. }
  1394. break;
  1395. case '>': // second bogus token in ->
  1396. case '.':
  1397. // skip a token to avoid foo->OpenFile, foo.OpenFile
  1398. scanner.GetToken();
  1399. break;
  1400. case eTokenTypeHazardousFunction:
  1401. {
  1402. if (scanner.m_hazardousFunction->function)
  1403. {
  1404. const char* message = scanner.m_hazardousFunction->function(scanner);
  1405. if (message)
  1406. {
  1407. total += 1;
  1408. scanner.Warn(message);
  1409. }
  1410. }
  1411. else if (scanner.m_hazardousFunction->message)
  1412. {
  1413. total += 1;
  1414. scanner.Warn(scanner.m_hazardousFunction->message);
  1415. }
  1416. else
  1417. {
  1418. scanner.Warn();
  1419. BreakPoint();
  1420. }
  1421. }
  1422. break;
  1423. }
  1424. }
  1425. return total;
  1426. }
  1427. bool __stdcall Contains(const char* s, const char* set)
  1428. {
  1429. return s && *s && strcspn(s, set) != strlen(s);
  1430. }
  1431. bool ContainsSlashes(const char* s) { return Contains(s, "\\/"); }
  1432. bool ContainsSlash(const char* s) { return ContainsSlashes(s); }
  1433. bool ContainsWildcards(const char* s) { return Contains(s, "*?"); }
  1434. bool IsSlash(int ch) { return (ch == '\\' || ch == '/'); }
  1435. std::string PathAppend(const std::string& s, const std::string& t)
  1436. {
  1437. // why does string lack back()?
  1438. int sslash = IsSlash(*(s.end() - 1)) ? 2 : 0;
  1439. int tslash = IsSlash(*t.begin()) ? 1 : 0;
  1440. switch (sslash | tslash)
  1441. {
  1442. case 0:
  1443. return (s + '\\' + t);
  1444. case 1:
  1445. case 2:
  1446. return (s + t);
  1447. case 3:
  1448. return (s + t.substr(1));
  1449. }
  1450. return std::string();
  1451. }
  1452. void __stdcall PathSplitOffLast(const std::string& s, std::string* a, std::string* b)
  1453. {
  1454. std::string::size_type slash = s.find_last_of("\\/");
  1455. *a = s.substr(0, slash);
  1456. *b = s.substr(slash + 1);
  1457. }
  1458. std::string __stdcall PathRemoveLastElement(const std::string& s)
  1459. {
  1460. return s.substr(0, s.find_last_of("\\/"));
  1461. }
  1462. bool __stdcall IsDotOrDotDot(const char* s)
  1463. {
  1464. return s[0] == '.' && (s[1] == 0 || (s[1] == '.' && s[2] == 0));
  1465. }
  1466. void CSharingHazardCheck::ProcessArgs(int argc, char** argv, std::vector<std::string>& files)
  1467. {
  1468. int i;
  1469. bool fRecurse = false;
  1470. std::vector<std::string> directories;
  1471. std::vector<std::string> genericWildcards;
  1472. std::vector<std::string> particularWildcards;
  1473. bool fWarnEmptyWildcards = true;
  1474. std::vector<char> fullpath;
  1475. std::vector<char> currentDirectory;
  1476. const DWORD SIZE = (1U << 16);
  1477. fullpath.resize(SIZE);
  1478. fullpath[0] = 0;
  1479. currentDirectory.resize(SIZE);
  1480. currentDirectory[0] = 0;
  1481. GetCurrentDirectory(SIZE, &currentDirectory[0]);
  1482. for (i = 1 ; i < argc ; ++i)
  1483. {
  1484. switch (argv[i][0])
  1485. {
  1486. default:
  1487. if (ContainsWildcards(argv[i]))
  1488. {
  1489. if (ContainsSlash(argv[i]))
  1490. {
  1491. // these will only be applied once, in whatever
  1492. // path they specifically refer
  1493. if (GetFullPathName(argv[i], SIZE, &fullpath[0], NULL))
  1494. {
  1495. particularWildcards.push_back(&fullpath[0]);
  1496. }
  1497. else
  1498. {
  1499. printf("GetFullPathName failed %s\n", argv[i]);
  1500. }
  1501. }
  1502. else
  1503. {
  1504. // do NOT call GetFullPathName here
  1505. genericWildcards.push_back(argv[i]);
  1506. }
  1507. }
  1508. else
  1509. {
  1510. if (GetFullPathName(argv[i], SIZE, &fullpath[0], NULL))
  1511. {
  1512. DWORD dwFileAttributes = GetFileAttributes(&fullpath[0]);
  1513. if (dwFileAttributes != 0xFFFFFFFF)
  1514. {
  1515. if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  1516. {
  1517. directories.push_back(&fullpath[0]);
  1518. }
  1519. else
  1520. {
  1521. files.push_back(&fullpath[0]);
  1522. }
  1523. }
  1524. else
  1525. {
  1526. printf("%s nonexistant\n", &fullpath[0]);
  1527. }
  1528. }
  1529. else
  1530. printf("GetFullPathName failed %s\n", argv[i]);
  1531. }
  1532. break;
  1533. /*
  1534. default output
  1535. file(line):reason
  1536. -recurse
  1537. -print-line
  1538. prints line of code with offending function after file(line):reason
  1539. -print-statement
  1540. print statement containing offending function after file(line):reason
  1541. supersedes -print-line
  1542. -print-context-lines:n
  1543. -print-statement plus surrounding n lines (not implemented)
  1544. -print-context-statements:n
  1545. -print-statement plus surrounding n "statements" (count semis and braces)
  1546. file names apply across all directories recursed into
  1547. wild cards apply across all directories recursed into
  1548. naming a directory implies one level recursion (unless -recurse is also seen)
  1549. environment variable SHARING_HAZARD_CHECK_OPTIONS added to argv (not implemented)
  1550. all directory walking happens before any output is generated
  1551. */
  1552. case '-':
  1553. case '/':
  1554. argv[i] += 1;
  1555. static const char szRecurse[] = "recurse";
  1556. static const char szPrintStatement[] = "print-statement";
  1557. static const char szPrintLine[] = "print-line";
  1558. static const char szPrintContextLines[] = "print-context-lines";
  1559. static const char szPrintContextStatements[] = "print-context-statements";
  1560. switch (argv[i][0])
  1561. {
  1562. default:
  1563. printf("unknown switch %s\n", argv[i]);
  1564. break;
  1565. case 'r': case 'R':
  1566. if (0 == _stricmp(argv[i]+1, 1 + szRecurse))
  1567. {
  1568. fRecurse = true;
  1569. }
  1570. break;
  1571. case 'p': case 'P':
  1572. if (0 == _strnicmp(argv[i]+1, "rint-", 5))
  1573. {
  1574. if (0 == _stricmp(
  1575. 6 + argv[i],
  1576. 6 + szPrintLine
  1577. ))
  1578. {
  1579. g_fPrintLine = true;
  1580. }
  1581. else if (0 == _stricmp(
  1582. 6 + argv[i],
  1583. 6 + szPrintStatement
  1584. ))
  1585. {
  1586. g_fPrintFullStatement = true;
  1587. g_fPrintLine = true;
  1588. }
  1589. else if (0 == _strnicmp(
  1590. 6 + argv[i],
  1591. 6 + szPrintContextLines,
  1592. NUMBER_OF(szPrintContextLines) - 6
  1593. ))
  1594. {
  1595. printf("unimplemented switch %s\n", argv[i]);
  1596. }
  1597. else if (0 == _strnicmp(
  1598. 6 + argv[i],
  1599. 6 + szPrintContextStatements,
  1600. NUMBER_OF(szPrintContextLines) - 6))
  1601. {
  1602. if (argv[i][NUMBER_OF(szPrintContextStatements)-1] == ':')
  1603. {
  1604. g_nPrintContextStatements = atoi(argv[i] + NUMBER_OF(szPrintContextStatements));
  1605. g_nPrintContextStatements = std::min<int>(g_nPrintContextStatements, NUMBER_OF(CClass().m_statements));
  1606. g_nPrintContextStatements = std::max<int>(g_nPrintContextStatements, 1);
  1607. }
  1608. else
  1609. {
  1610. g_nPrintContextStatements = 2;
  1611. }
  1612. g_fPrintFullStatement = true;
  1613. g_fPrintLine = true;
  1614. }
  1615. }
  1616. break;
  1617. }
  1618. break;
  1619. }
  1620. }
  1621. if (fRecurse)
  1622. {
  1623. // split up particular wildcards into directories and generic wildcards
  1624. for (std::vector<std::string>::const_iterator j = particularWildcards.begin();
  1625. j != particularWildcards.end();
  1626. ++j
  1627. )
  1628. {
  1629. std::string path;
  1630. std::string wild;
  1631. PathSplitOffLast(*j, &path, &wild);
  1632. directories.push_back(path);
  1633. genericWildcards.push_back(wild);
  1634. //printf("%s%s -> %s %s\n", path.c_str(), wild.c_str(), path.c_str(), wild.c_str());
  1635. }
  1636. particularWildcards.clear();
  1637. }
  1638. // empty command line produces nothing, by design
  1639. if (genericWildcards.empty()
  1640. && !directories.empty()
  1641. )
  1642. {
  1643. genericWildcards.push_back("*");
  1644. }
  1645. else if (
  1646. directories.empty()
  1647. && files.empty()
  1648. && !genericWildcards.empty())
  1649. {
  1650. directories.push_back(&currentDirectory[0]);
  1651. }
  1652. if (!directories.empty()
  1653. || !genericWildcards.empty()
  1654. || !particularWildcards.empty()
  1655. || fRecurse
  1656. )
  1657. {
  1658. // if you don't output the \n and let the .s word wrap,
  1659. // f4 in the output gets messed up wrt which line gets highlighted
  1660. printf("processing argv..\n"); fflush(stdout);
  1661. #define ARGV_PROGRESS() printf("."); fflush(stdout);
  1662. #undef ARGV_PROGRESS
  1663. #define ARGV_PROGRESS() printf("processing argv..\n"); fflush(stdout);
  1664. #undef ARGV_PROGRESS
  1665. #define ARGV_PROGRESS() /* nothing */
  1666. }
  1667. WIN32_FIND_DATA findData;
  1668. if (!directories.empty())
  1669. {
  1670. std::set<std::string> allDirectoriesSeen; // avoid repeats when recursing
  1671. std::stack<std::string> stack;
  1672. for (std::vector<std::string>::const_iterator k = directories.begin();
  1673. k != directories.end();
  1674. ++k
  1675. )
  1676. {
  1677. stack.push(*k);
  1678. }
  1679. while (!stack.empty())
  1680. {
  1681. std::string directory = stack.top();
  1682. stack.pop();
  1683. if (!fRecurse || allDirectoriesSeen.find(directory) == allDirectoriesSeen.end())
  1684. {
  1685. if (fRecurse)
  1686. {
  1687. allDirectoriesSeen.insert(allDirectoriesSeen.end(), directory);
  1688. }
  1689. for
  1690. (
  1691. std::vector<std::string>::const_iterator w = genericWildcards.begin();
  1692. w != genericWildcards.end();
  1693. ++w
  1694. )
  1695. {
  1696. std::string file = PathAppend(directory, *w);
  1697. CFindFile findFile;
  1698. if (SUCCEEDED(findFile.HrCreate(file.c_str(), &findData)))
  1699. {
  1700. fWarnEmptyWildcards = false;
  1701. ARGV_PROGRESS();
  1702. // only match files here
  1703. do
  1704. {
  1705. if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
  1706. {
  1707. files.push_back(PathAppend(directory, findData.cFileName));
  1708. }
  1709. } while (FindNextFile(findFile, &findData));
  1710. }
  1711. else
  1712. {
  1713. if (fWarnEmptyWildcards)
  1714. printf("warning: %s expanded to nothing\n", file.c_str());
  1715. }
  1716. if (fRecurse)
  1717. {
  1718. // only match directories here
  1719. std::string star = PathAppend(directory, "*");
  1720. CFindFile findFile;
  1721. if (SUCCEEDED(findFile.HrCreate(star.c_str(), &findData)))
  1722. {
  1723. fWarnEmptyWildcards = false;
  1724. ARGV_PROGRESS();
  1725. do
  1726. {
  1727. if (!IsDotOrDotDot(findData.cFileName)
  1728. && (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  1729. {
  1730. stack.push(PathAppend(directory, findData.cFileName));
  1731. }
  1732. } while (FindNextFile(findFile, &findData));
  1733. }
  1734. else
  1735. {
  1736. if (fWarnEmptyWildcards)
  1737. printf("warning: %s expanded to nothing\n", star.c_str());
  1738. }
  1739. }
  1740. }
  1741. }
  1742. }
  1743. }
  1744. // particular wildcards only match files, not directories
  1745. for
  1746. (
  1747. std::vector<std::string>::const_iterator w = particularWildcards.begin();
  1748. w != particularWildcards.end();
  1749. ++w
  1750. )
  1751. {
  1752. std::string directory = PathRemoveLastElement(*w);
  1753. CFindFile findFile;
  1754. if (SUCCEEDED(findFile.HrCreate(w->c_str(), &findData)))
  1755. {
  1756. fWarnEmptyWildcards = false;
  1757. ARGV_PROGRESS();
  1758. // only match files here
  1759. do
  1760. {
  1761. if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
  1762. {
  1763. files.push_back(PathAppend(directory, findData.cFileName));
  1764. }
  1765. } while (FindNextFile(findFile, &findData));
  1766. }
  1767. else
  1768. {
  1769. // if (fWarnEmptyWildcards) // always warn for "particular wildcards"
  1770. printf("warning: %s expanded to nothing\n", w->c_str());
  1771. }
  1772. }
  1773. std::sort(files.begin(), files.end());
  1774. files.resize(std::unique(files.begin(), files.end()) - files.begin());
  1775. printf("\n");
  1776. }
  1777. void CSharingHazardCheck::Main(int argc, char** argv)
  1778. {
  1779. if (argc < 2)
  1780. {
  1781. printf(usage, argv[0]);
  1782. exit(EXIT_FAILURE);
  1783. }
  1784. fputs(banner, stdout);
  1785. std::vector<std::string> files;
  1786. ProcessArgs(argc, argv, files);
  1787. InitTables();
  1788. int total = 0;
  1789. for (
  1790. std::vector<std::string>::const_iterator i = files.begin();
  1791. i != files.end();
  1792. ++i
  1793. )
  1794. {
  1795. total += ProcessFile(*i);
  1796. }
  1797. printf("\n%d warnings\n", total);
  1798. }
  1799. int __cdecl main(int argc, char** argv)
  1800. {
  1801. app.Main(argc, argv);
  1802. return 0;
  1803. }