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