• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 syzkaller project authors. All rights reserved.
2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
3 
4 // This is a very rough prototype of an utility that extracts syscall descriptions from header files.
5 // It needs to extract struct/union descriptions, better analyze types,
6 // analyze pointer directions (in, out), figure out len types (usually marked with sal).
7 // The easiest way to build it is to build it as part of clang. Add the following lines to CMakeLists.txt:
8 //   +add_clang_executable(syz-declextract syz-declextract/syz-declextract.cpp)
9 //   +target_link_libraries(syz-declextract clangTooling)
10 // It was used to extract windows descriptions:
11 //   syz-declextract -extra-arg="--driver-mode=cl" -extra-arg="-I/path/to/windows/headers" Windows.h
12 
13 #include "clang/AST/AST.h"
14 #include "clang/AST/ASTConsumer.h"
15 #include "clang/AST/ASTContext.h"
16 #include "clang/AST/RecursiveASTVisitor.h"
17 #include "clang/Driver/Options.h"
18 #include "clang/Frontend/ASTConsumers.h"
19 #include "clang/Frontend/CompilerInstance.h"
20 #include "clang/Frontend/FrontendActions.h"
21 #include "clang/Rewrite/Core/Rewriter.h"
22 #include "clang/Tooling/CommonOptionsParser.h"
23 #include "clang/Tooling/Tooling.h"
24 
25 using namespace clang;
26 using namespace clang::tooling;
27 
convertType(ASTContext & C,QualType T)28 std::string convertType(ASTContext &C, QualType T) {
29   auto name = T.getAsString();
30   if (name == "HANDLE")
31     return name;
32   if (T->isIntegralOrEnumerationType()) {
33     int size = C.getTypeSize(T);
34     char buf[10];
35     sprintf(buf, "int%d", size);
36     return buf;
37   }
38   if (T->isVoidPointerType()) {
39     return "ptr[inout, array[int8]]";
40   }
41   if (T->isPointerType()) {
42     auto inner = convertType(C, T->getPointeeType());
43     if (inner == "")
44       return "ptr[inout, array[int8]]";
45     char buf[1024];
46     sprintf(buf, "ptr[inout, %s]", inner.c_str());
47     return buf;
48   }
49   return "intptr";
50 }
51 
52 class DeclExtractCallVisitor : public RecursiveASTVisitor<DeclExtractCallVisitor> {
53  public:
DeclExtractCallVisitor(ASTContext * Context)54   explicit DeclExtractCallVisitor(ASTContext *Context)
55       : Context(*Context) {}
56 
VisitFunctionDecl(const FunctionDecl * D)57   bool VisitFunctionDecl(const FunctionDecl *D) {
58     if (D->doesThisDeclarationHaveABody())
59       return true;
60     // TODO(dvyukov): need to select only stdcall (WINAPI) functions.
61     // But the following 2 approaches do not work.
62     if (false) {
63       if (auto *FPT = D->getType()->getAs<FunctionProtoType>()) {
64         if (FPT->getExtInfo().getCC() != CC_X86StdCall)
65           return true;
66       }
67     }
68     if (false) {
69       if (!D->hasAttr<StdCallAttr>())
70         return true;
71     }
72     // Tons of functions are bulk ignored below because they cause
73     // static/dynamic link failures, reboot machine, etc.
74     auto fn = D->getNameInfo().getAsString();
75     if (fn.empty()) return true;
76     if (*fn.rbegin() == 'W') return true; // Unicode versions.
77     const char *ignore_prefixes[] {
78       "_",
79       "Rtl",
80       "IBind",
81       "Ndr",
82       "NDR",
83       "SCard",
84     };
85     for (auto prefix: ignore_prefixes) {
86       if (strncmp(fn.c_str(), prefix, strlen(prefix)) == 0) return true;
87     }
88     const char *ignore_functions[] {
89       "IEnum",
90       "IStream",
91       "IType",
92       "IService",
93       "IProperty",
94       "ISequential",
95       "IDispatch",
96       "I_RPC",
97       "I_Rpc",
98       "CLEANLOCAL",
99       "WinMain",
100       "PropertySheet",
101       "LookupAccountNameLocalA",
102       "LookupAccountSidLocalA",
103       "WTSGetServiceSessionId",
104       "WTSIsServerContainer",
105       "GetDisplayAutoRotationPreferencesByProcessId",
106       "LoadStringByReference",
107       "IdnToNameprepUnicode",
108       "VerFindFileA",
109       "VerInstallFileA",
110       "GetFileVersionInfoSizeA",
111       "GetFileVersionInfoA",
112       "GetFileVersionInfoSizeExA",
113       "GetFileVersionInfoExA",
114       "VerQueryValueA",
115       "sndOpenSound",
116       "Netbios",
117       "RpcBindingGetTrainingContextHandle",
118       "RpcAsyncCleanupThread",
119       "ShellMessageBoxA",
120       "SHEnumerateUnreadMailAccountsA",
121       "SHGetUnreadMailCountA",
122       "SHSetUnreadMailCountA",
123       "GetEncSChannel",
124       "CryptExportPKCS8Ex",
125       "FindCertsByIssuer",
126       "CryptCancelAsyncRetrieval",
127       "CryptGetTimeValidObject",
128       "CryptFlushTimeValidObject",
129       "CryptProtectDataNoUI",
130       "CryptUnprotectDataNoUI",
131       "NsServerBindSearch",
132       "NsClientBindSearch",
133       "NsClientBindDone",
134       "GetOpenCardNameA",
135       "SubscribeServiceChangeNotifications",
136       "UnsubscribeServiceChangeNotifications",
137       "GetThreadDescription",
138       "SetThreadDescription",
139       "DialogControlDpi",
140       "SetDialogDpiChangeBehavior",
141       "GetDialogDpiChangeBehavior",
142       "RpcServer",
143       "DecodePointer",
144       "DecodeRemotePointer",
145       "DecodeSystemPointer",
146       "EncodePointer",
147       "EncodeRemotePointer",
148       "EncodeSystemPointer",
149       "UnmapViewOfFile2",
150       "MapViewOfFileNuma2",
151       "DeriveCapabilitySidsFromName",
152       "QueryAuxiliaryCounterFrequency",
153       "ConvertPerformanceCounterToAuxiliaryCounter",
154       "ConvertAuxiliaryCounterToPerformanceCounter",
155       "FreePropVariantArray",
156       "PropVariantCopy",
157       "PropVariantClear",
158       "InitiateShutdown",
159       "ExitWindowsEx",
160       "LockWorkStation",
161       "InitiateSystemShutdown",
162       "InitiateSystemShutdownEx",
163       "shutdown",
164     };
165     for (auto func: ignore_functions) {
166       if (strstr(fn.c_str(), func)) return true;
167     }
168     // These are already described:
169     const char *ignore_exact[] {
170       "CreateFileA",
171       "CloseHandle",
172       "VirtualAlloc",
173     };
174     for (auto func: ignore_exact) {
175       if (strcmp(fn.c_str(), func) == 0) return true;
176     }
177     const char *ignore_files[] {
178       "/um/ole",
179       "htiface.h",
180       "objbase.h",
181       "HLink.h",
182       "urlmon.h",
183       "HlGuids.h",
184       "unknwn.h",
185       "unknwnbase.h",
186       "coguid.h",
187       "MsHtmHst.h",
188       "msime.h",
189       "ComSvcs.h",
190       "combaseapi.h",
191       "WbemGlue.h",
192       "OCIdl.h",
193       "mfapi.h",
194       "CompPkgSup.h",
195       "ole2.h",
196       "Ole2.h",
197       "oleidl.h",
198       "ObjIdl.h",
199       "WabDefs.h",
200       "objidl.h",
201     };
202     auto src = D->getSourceRange().getBegin().printToString(Context.getSourceManager());
203     if (strstr(src.c_str(), "/um/") == 0) return true;
204     for (auto file: ignore_files) {
205       if (strstr(src.c_str(), file)) return true;
206     }
207     for (const ParmVarDecl *P : D->parameters()) {
208       auto typ = convertType(Context, P->getType());
209       if (typ == "") {
210         llvm::outs() << D->getNameInfo().getAsString() << ": UNKNOWN TYPE: " <<
211             QualType(P->getType()).getAsString() << "\n";
212         return true;
213       }
214     }
215     if (Generated[D->getNameInfo().getAsString()])
216       return true;
217     Generated[D->getNameInfo().getAsString()] = true;
218 
219     llvm::outs() << D->getNameInfo().getAsString() << "(";
220     int i = 0;
221     for (const ParmVarDecl *P : D->parameters()) {
222       if (i)
223         llvm::outs() << ", ";
224       auto name = P->getNameAsString();
225       if (name == "") {
226         char buf[10];
227         sprintf(buf, "arg%d", i);
228         name = buf;
229       }
230       llvm::outs() << name << " " << convertType(Context, P->getType());
231       i++;
232       if (i == 9)
233         break;
234     }
235     llvm::outs() << ")";
236     auto ret = convertType(Context, D->getReturnType());
237     if (ret == "HANDLE")
238       llvm::outs() << " " << ret;
239     llvm::outs() << "\n";
240     return true;
241   }
242 
243  private:
244   ASTContext &Context;
245   std::map<std::string, bool> Generated;
246 };
247 
248 class DeclExtractCallConsumer : public clang::ASTConsumer {
249  public:
DeclExtractCallConsumer(ASTContext * Context)250   explicit DeclExtractCallConsumer(ASTContext *Context)
251       : Visitor(Context) {}
252 
HandleTranslationUnit(clang::ASTContext & Context)253   virtual void HandleTranslationUnit(clang::ASTContext &Context) {
254     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
255   }
256 
257  private:
258   DeclExtractCallVisitor Visitor;
259 };
260 
261 class DeclExtractCallAction : public clang::ASTFrontendAction {
262  public:
DeclExtractCallAction()263   DeclExtractCallAction() {}
264 
CreateASTConsumer(clang::CompilerInstance & Compiler,llvm::StringRef InFile)265   virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
266       clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
267     return std::unique_ptr<clang::ASTConsumer>(
268         new DeclExtractCallConsumer(&Compiler.getASTContext()));
269   }
270 };
271 
272 static llvm::cl::OptionCategory MyToolCategory("my-tool options");
273 
main(int argc,const char ** argv)274 int main(int argc, const char **argv) {
275   CommonOptionsParser OptionsParser(argc, argv, MyToolCategory);
276   ClangTool Tool(OptionsParser.getCompilations(),
277                  OptionsParser.getSourcePathList());
278   return Tool.run(newFrontendActionFactory<DeclExtractCallAction>().get());
279 }
280