| // Copyright 2017 syzkaller project authors. All rights reserved. |
| // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. |
| |
| // This is a very rough prototype of an utility that extracts syscall descriptions from header files. |
| // It needs to extract struct/union descriptions, better analyze types, |
| // analyze pointer directions (in, out), figure out len types (usually marked with sal). |
| // The easiest way to build it is to build it as part of clang. Add the following lines to CMakeLists.txt: |
| // +add_clang_executable(syz-declextract syz-declextract/syz-declextract.cpp) |
| // +target_link_libraries(syz-declextract clangTooling) |
| // It was used to extract windows descriptions: |
| // syz-declextract -extra-arg="--driver-mode=cl" -extra-arg="-I/path/to/windows/headers" Windows.h |
| |
| #include "clang/AST/AST.h" |
| #include "clang/AST/ASTConsumer.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/RecursiveASTVisitor.h" |
| #include "clang/Driver/Options.h" |
| #include "clang/Frontend/ASTConsumers.h" |
| #include "clang/Frontend/CompilerInstance.h" |
| #include "clang/Frontend/FrontendActions.h" |
| #include "clang/Rewrite/Core/Rewriter.h" |
| #include "clang/Tooling/CommonOptionsParser.h" |
| #include "clang/Tooling/Tooling.h" |
| |
| using namespace clang; |
| using namespace clang::tooling; |
| |
| std::string convertType(ASTContext &C, QualType T) { |
| auto name = T.getAsString(); |
| if (name == "HANDLE") |
| return name; |
| if (T->isIntegralOrEnumerationType()) { |
| int size = C.getTypeSize(T); |
| char buf[10]; |
| sprintf(buf, "int%d", size); |
| return buf; |
| } |
| if (T->isVoidPointerType()) { |
| return "ptr[inout, array[int8]]"; |
| } |
| if (T->isPointerType()) { |
| auto inner = convertType(C, T->getPointeeType()); |
| if (inner == "") |
| return "ptr[inout, array[int8]]"; |
| char buf[1024]; |
| sprintf(buf, "ptr[inout, %s]", inner.c_str()); |
| return buf; |
| } |
| return "intptr"; |
| } |
| |
| class DeclExtractCallVisitor : public RecursiveASTVisitor<DeclExtractCallVisitor> { |
| public: |
| explicit DeclExtractCallVisitor(ASTContext *Context) |
| : Context(*Context) {} |
| |
| bool VisitFunctionDecl(const FunctionDecl *D) { |
| if (D->doesThisDeclarationHaveABody()) |
| return true; |
| // TODO(dvyukov): need to select only stdcall (WINAPI) functions. |
| // But the following 2 approaches do not work. |
| if (false) { |
| if (auto *FPT = D->getType()->getAs<FunctionProtoType>()) { |
| if (FPT->getExtInfo().getCC() != CC_X86StdCall) |
| return true; |
| } |
| } |
| if (false) { |
| if (!D->hasAttr<StdCallAttr>()) |
| return true; |
| } |
| // Tons of functions are bulk ignored below because they cause |
| // static/dynamic link failures, reboot machine, etc. |
| auto fn = D->getNameInfo().getAsString(); |
| if (fn.empty()) return true; |
| if (*fn.rbegin() == 'W') return true; // Unicode versions. |
| const char *ignore_prefixes[] { |
| "_", |
| "Rtl", |
| "IBind", |
| "Ndr", |
| "NDR", |
| "SCard", |
| }; |
| for (auto prefix: ignore_prefixes) { |
| if (strncmp(fn.c_str(), prefix, strlen(prefix)) == 0) return true; |
| } |
| const char *ignore_functions[] { |
| "IEnum", |
| "IStream", |
| "IType", |
| "IService", |
| "IProperty", |
| "ISequential", |
| "IDispatch", |
| "I_RPC", |
| "I_Rpc", |
| "CLEANLOCAL", |
| "WinMain", |
| "PropertySheet", |
| "LookupAccountNameLocalA", |
| "LookupAccountSidLocalA", |
| "WTSGetServiceSessionId", |
| "WTSIsServerContainer", |
| "GetDisplayAutoRotationPreferencesByProcessId", |
| "LoadStringByReference", |
| "IdnToNameprepUnicode", |
| "VerFindFileA", |
| "VerInstallFileA", |
| "GetFileVersionInfoSizeA", |
| "GetFileVersionInfoA", |
| "GetFileVersionInfoSizeExA", |
| "GetFileVersionInfoExA", |
| "VerQueryValueA", |
| "sndOpenSound", |
| "Netbios", |
| "RpcBindingGetTrainingContextHandle", |
| "RpcAsyncCleanupThread", |
| "ShellMessageBoxA", |
| "SHEnumerateUnreadMailAccountsA", |
| "SHGetUnreadMailCountA", |
| "SHSetUnreadMailCountA", |
| "GetEncSChannel", |
| "CryptExportPKCS8Ex", |
| "FindCertsByIssuer", |
| "CryptCancelAsyncRetrieval", |
| "CryptGetTimeValidObject", |
| "CryptFlushTimeValidObject", |
| "CryptProtectDataNoUI", |
| "CryptUnprotectDataNoUI", |
| "NsServerBindSearch", |
| "NsClientBindSearch", |
| "NsClientBindDone", |
| "GetOpenCardNameA", |
| "SubscribeServiceChangeNotifications", |
| "UnsubscribeServiceChangeNotifications", |
| "GetThreadDescription", |
| "SetThreadDescription", |
| "DialogControlDpi", |
| "SetDialogDpiChangeBehavior", |
| "GetDialogDpiChangeBehavior", |
| "RpcServer", |
| "DecodePointer", |
| "DecodeRemotePointer", |
| "DecodeSystemPointer", |
| "EncodePointer", |
| "EncodeRemotePointer", |
| "EncodeSystemPointer", |
| "UnmapViewOfFile2", |
| "MapViewOfFileNuma2", |
| "DeriveCapabilitySidsFromName", |
| "QueryAuxiliaryCounterFrequency", |
| "ConvertPerformanceCounterToAuxiliaryCounter", |
| "ConvertAuxiliaryCounterToPerformanceCounter", |
| "FreePropVariantArray", |
| "PropVariantCopy", |
| "PropVariantClear", |
| "InitiateShutdown", |
| "ExitWindowsEx", |
| "LockWorkStation", |
| "InitiateSystemShutdown", |
| "InitiateSystemShutdownEx", |
| "shutdown", |
| }; |
| for (auto func: ignore_functions) { |
| if (strstr(fn.c_str(), func)) return true; |
| } |
| // These are already described: |
| const char *ignore_exact[] { |
| "CreateFileA", |
| "CloseHandle", |
| "VirtualAlloc", |
| }; |
| for (auto func: ignore_exact) { |
| if (strcmp(fn.c_str(), func) == 0) return true; |
| } |
| const char *ignore_files[] { |
| "/um/ole", |
| "htiface.h", |
| "objbase.h", |
| "HLink.h", |
| "urlmon.h", |
| "HlGuids.h", |
| "unknwn.h", |
| "unknwnbase.h", |
| "coguid.h", |
| "MsHtmHst.h", |
| "msime.h", |
| "ComSvcs.h", |
| "combaseapi.h", |
| "WbemGlue.h", |
| "OCIdl.h", |
| "mfapi.h", |
| "CompPkgSup.h", |
| "ole2.h", |
| "Ole2.h", |
| "oleidl.h", |
| "ObjIdl.h", |
| "WabDefs.h", |
| "objidl.h", |
| }; |
| auto src = D->getSourceRange().getBegin().printToString(Context.getSourceManager()); |
| if (strstr(src.c_str(), "/um/") == 0) return true; |
| for (auto file: ignore_files) { |
| if (strstr(src.c_str(), file)) return true; |
| } |
| for (const ParmVarDecl *P : D->parameters()) { |
| auto typ = convertType(Context, P->getType()); |
| if (typ == "") { |
| llvm::outs() << D->getNameInfo().getAsString() << ": UNKNOWN TYPE: " << |
| QualType(P->getType()).getAsString() << "\n"; |
| return true; |
| } |
| } |
| if (Generated[D->getNameInfo().getAsString()]) |
| return true; |
| Generated[D->getNameInfo().getAsString()] = true; |
| |
| llvm::outs() << D->getNameInfo().getAsString() << "("; |
| int i = 0; |
| for (const ParmVarDecl *P : D->parameters()) { |
| if (i) |
| llvm::outs() << ", "; |
| auto name = P->getNameAsString(); |
| if (name == "") { |
| char buf[10]; |
| sprintf(buf, "arg%d", i); |
| name = buf; |
| } |
| llvm::outs() << name << " " << convertType(Context, P->getType()); |
| i++; |
| if (i == 9) |
| break; |
| } |
| llvm::outs() << ")"; |
| auto ret = convertType(Context, D->getReturnType()); |
| if (ret == "HANDLE") |
| llvm::outs() << " " << ret; |
| llvm::outs() << "\n"; |
| return true; |
| } |
| |
| private: |
| ASTContext &Context; |
| std::map<std::string, bool> Generated; |
| }; |
| |
| class DeclExtractCallConsumer : public clang::ASTConsumer { |
| public: |
| explicit DeclExtractCallConsumer(ASTContext *Context) |
| : Visitor(Context) {} |
| |
| virtual void HandleTranslationUnit(clang::ASTContext &Context) { |
| Visitor.TraverseDecl(Context.getTranslationUnitDecl()); |
| } |
| |
| private: |
| DeclExtractCallVisitor Visitor; |
| }; |
| |
| class DeclExtractCallAction : public clang::ASTFrontendAction { |
| public: |
| DeclExtractCallAction() {} |
| |
| virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer( |
| clang::CompilerInstance &Compiler, llvm::StringRef InFile) { |
| return std::unique_ptr<clang::ASTConsumer>( |
| new DeclExtractCallConsumer(&Compiler.getASTContext())); |
| } |
| }; |
| |
| static llvm::cl::OptionCategory MyToolCategory("my-tool options"); |
| |
| int main(int argc, const char **argv) { |
| CommonOptionsParser OptionsParser(argc, argv, MyToolCategory); |
| ClangTool Tool(OptionsParser.getCompilations(), |
| OptionsParser.getSourcePathList()); |
| return Tool.run(newFrontendActionFactory<DeclExtractCallAction>().get()); |
| } |