diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2024-12-13 15:15:49 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2024-12-17 13:44:24 +0000 |
| commit | c8c15bb214509bafc8fe1a1e3abb8ccf90b3306e (patch) | |
| tree | ca722a71aff5a1566389f178d9c95d7d7e8caeed /tools | |
| parent | bc1a1b50f942408a9139887b914f745d9fa02adc (diff) | |
tools/syz-declextract: infer argument/field types
Use data flow analysis to infer syscall argument, return value,
and struct field types.
See the comment in pkg/declextract/typing.go for more details.
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/syz-declextract/clangtool/declextract.cpp | 189 | ||||
| -rw-r--r-- | tools/syz-declextract/clangtool/json.h | 1 | ||||
| -rw-r--r-- | tools/syz-declextract/clangtool/output.h | 110 | ||||
| -rw-r--r-- | tools/syz-declextract/declextract.go | 11 | ||||
| -rw-r--r-- | tools/syz-declextract/declextract_test.go | 10 | ||||
| -rw-r--r-- | tools/syz-declextract/testdata/functions.c | 45 | ||||
| -rw-r--r-- | tools/syz-declextract/testdata/functions.c.info | 2 | ||||
| -rw-r--r-- | tools/syz-declextract/testdata/functions.c.json | 231 | ||||
| -rw-r--r-- | tools/syz-declextract/testdata/functions.c.txt | 2 | ||||
| -rw-r--r-- | tools/syz-declextract/testdata/types.c | 11 | ||||
| -rw-r--r-- | tools/syz-declextract/testdata/types.c.json | 105 |
11 files changed, 678 insertions, 39 deletions
diff --git a/tools/syz-declextract/clangtool/declextract.cpp b/tools/syz-declextract/clangtool/declextract.cpp index d2b2133b9..c41904d8b 100644 --- a/tools/syz-declextract/clangtool/declextract.cpp +++ b/tools/syz-declextract/clangtool/declextract.cpp @@ -117,7 +117,7 @@ private: std::string getDeclName(const Expr* Expr); const ValueDecl* getValueDecl(const Expr* Expr); std::string getDeclFileID(const Decl* Decl); - std::string policyName(const ValueDecl* Decl); + std::string getUniqueDeclName(const NamedDecl* Decl); std::vector<std::pair<int, std::string>> extractDesignatedInitConsts(const VarDecl& ArrayDecl); FieldType genType(QualType Typ, const std::string& BackupName = ""); std::unordered_map<std::string, unsigned> structFieldIndexes(const RecordDecl* Decl); @@ -129,6 +129,12 @@ private: std::optional<QualType> getSizeofType(const Expr* E); int sizeofType(const Type* T); std::vector<IoctlCmd> extractIoctlCommands(const std::string& Ioctl); + std::optional<TypingEntity> getTypingEntity(const std::string& CurrentFunc, + std::unordered_map<const VarDecl*, int>& LocalVars, + std::unordered_map<std::string, int>& LocalSeq, const Expr* E); + std::optional<TypingEntity> getDeclTypingEntity(const std::string& CurrentFunc, + std::unordered_map<const VarDecl*, int>& LocalVars, + std::unordered_map<std::string, int>& LocalSeq, const Decl* Decl); }; // PPCallbacksTracker records all macro definitions (name/value/source location). @@ -473,7 +479,7 @@ void Extractor::matchNetlinkPolicy() { std::unique_ptr<FieldType> Elem; if (AttrKind == "NLA_NESTED" || AttrKind == "NLA_NESTED_ARRAY") { if (const auto* NestedDecl = getValueDecl(AttrInit->getInit(2))) - NestedPolicy = policyName(NestedDecl); + NestedPolicy = getUniqueDeclName(NestedDecl); } else { MaxSize = evaluate<int>(LenExpr); if (auto SizeofType = getSizeofType(LenExpr)) @@ -488,7 +494,7 @@ void Extractor::matchNetlinkPolicy() { }); } Output.emit(NetlinkPolicy{ - .Name = policyName(PolicyArray), + .Name = getUniqueDeclName(PolicyArray), .Attrs = std::move(Attrs), }); } @@ -499,7 +505,7 @@ void Extractor::matchNetlinkFamily() { const std::string& FamilyName = llvm::dyn_cast<StringLiteral>(FamilyInit->getInit(Fields["name"]))->getString().str(); std::string DefaultPolicy; if (const auto* PolicyDecl = FamilyInit->getInit(Fields["policy"])->getAsBuiltinConstantDeclRef(*Context)) - DefaultPolicy = policyName(PolicyDecl); + DefaultPolicy = getUniqueDeclName(PolicyDecl); std::vector<NetlinkOp> Ops; for (const auto& OpsName : {"ops", "small_ops", "split_ops"}) { const auto* OpsDecl = @@ -521,7 +527,7 @@ void Extractor::matchNetlinkFamily() { std::string Policy; if (OpsFields.count("policy") != 0) { if (const auto* PolicyDecl = OpInit->getInit(OpsFields["policy"])->getAsBuiltinConstantDeclRef(*Context)) - Policy = policyName(PolicyDecl); + Policy = getUniqueDeclName(PolicyDecl); } if (Policy.empty()) Policy = DefaultPolicy; @@ -550,42 +556,185 @@ void Extractor::matchNetlinkFamily() { }); } -std::string Extractor::policyName(const ValueDecl* Decl) { return Decl->getNameAsString() + "_" + getDeclFileID(Decl); } +std::string Extractor::getUniqueDeclName(const NamedDecl* Decl) { + return Decl->getNameAsString() + "_" + getDeclFileID(Decl); +} + +const Expr* removeCasts(const Expr* E) { + for (;;) { + if (auto* P = dyn_cast<ParenExpr>(E)) + E = P->getSubExpr(); + else if (auto* C = dyn_cast<CastExpr>(E)) + E = C->getSubExpr(); + else + break; + } + return E; +} + +bool isInterestingCall(const CallExpr* Call) { + auto* CalleeDecl = Call->getDirectCallee(); + // We don't handle indirect calls yet. + if (!CalleeDecl) + return false; + // Builtins are not interesting and won't have a body. + if (CalleeDecl->getBuiltinID() != Builtin::ID::NotBuiltin) + return false; + const std::string& Callee = CalleeDecl->getNameAsString(); + // There are too many of these and they should only be called at runtime in broken builds. + if (Callee.rfind("__compiletime_assert", 0) == 0 || Callee == "____wrong_branch_error" || + Callee == "__bad_size_call_parameter") + return false; + return true; +} void Extractor::matchFunctionDef() { const auto* Func = getResult<FunctionDecl>("function"); + const std::string& CurrentFunc = Func->getNameAsString(); const auto Range = Func->getSourceRange(); const std::string& SourceFile = std::filesystem::relative(SourceManager->getFilename(SourceManager->getExpansionLoc(Range.getBegin())).str()); const int LOC = std::max<int>(0, SourceManager->getExpansionLineNumber(Range.getEnd()) - SourceManager->getExpansionLineNumber(Range.getBegin()) - 1); + std::vector<TypingFact> Facts; std::vector<std::string> Callees; std::unordered_set<std::string> CalleesDedup; + std::unordered_map<const VarDecl*, int> LocalVars; + std::unordered_map<std::string, int> LocalSeq; const auto& Calls = findAllMatches<CallExpr>(Func->getBody(), stmt(forEachDescendant(callExpr().bind("res")))); for (auto* Call : Calls) { - if (auto* CalleeDecl = Call->getDirectCallee()) { - // Builtins are not interesting and won't have a body. - if (CalleeDecl->getBuiltinID() != Builtin::ID::NotBuiltin) - continue; - const std::string& Callee = CalleeDecl->getNameAsString(); - // There are too many of these and they should only be called at runtime in broken builds. - if (Callee.rfind("__compiletime_assert", 0) == 0 || Callee == "____wrong_branch_error" || - Callee == "__bad_size_call_parameter") - continue; - if (!CalleesDedup.insert(Callee).second) - continue; - Callees.push_back(Callee); + if (!isInterestingCall(Call)) + continue; + const std::string& Callee = Call->getDirectCallee()->getNameAsString(); + for (unsigned AI = 0; AI < Call->getNumArgs(); AI++) { + if (auto Src = getTypingEntity(CurrentFunc, LocalVars, LocalSeq, Call->getArg(AI))) { + Facts.push_back({std::move(*Src), EntityArgument{ + .Func = Callee, + .Arg = AI, + }}); + } + } + if (!CalleesDedup.insert(Callee).second) + continue; + Callees.push_back(Callee); + } + const auto& Assignments = findAllMatches<BinaryOperator>( + Func->getBody(), stmt(forEachDescendant(binaryOperator(isAssignmentOperator()).bind("res")))); + for (auto* A : Assignments) { + auto Src = getTypingEntity(CurrentFunc, LocalVars, LocalSeq, A->getRHS()); + auto Dst = getTypingEntity(CurrentFunc, LocalVars, LocalSeq, A->getLHS()); + if (Src && Dst) + Facts.push_back({std::move(*Src), std::move(*Dst)}); + } + const auto& VarDecls = findAllMatches<VarDecl>( + Func->getBody(), stmt(forEachDescendant(varDecl(hasAutomaticStorageDuration()).bind("res")))); + for (auto* D : VarDecls) { + auto Src = getTypingEntity(CurrentFunc, LocalVars, LocalSeq, D->getInit()); + auto Dst = getDeclTypingEntity(CurrentFunc, LocalVars, LocalSeq, D); + if (Src && Dst) + Facts.push_back({std::move(*Src), std::move(*Dst)}); + } + const auto& Returns = findAllMatches<ReturnStmt>(Func->getBody(), stmt(forEachDescendant(returnStmt().bind("res")))); + for (auto* Ret : Returns) { + if (auto Src = getTypingEntity(CurrentFunc, LocalVars, LocalSeq, Ret->getRetValue())) { + Facts.push_back({std::move(*Src), EntityReturn{ + .Func = CurrentFunc, + }}); } } Output.emit(Function{ - .Name = Func->getNameAsString(), + .Name = CurrentFunc, .File = SourceFile, .IsStatic = Func->isStatic(), .LOC = LOC, .Calls = std::move(Callees), + .Facts = std::move(Facts), }); } +std::optional<TypingEntity> Extractor::getTypingEntity(const std::string& CurrentFunc, + std::unordered_map<const VarDecl*, int>& LocalVars, + std::unordered_map<std::string, int>& LocalSeq, const Expr* E) { + if (!E) + return {}; + E = removeCasts(E); + if (auto* DeclRef = dyn_cast<DeclRefExpr>(E)) { + return getDeclTypingEntity(CurrentFunc, LocalVars, LocalSeq, DeclRef->getDecl()); + } else if (auto* Member = dyn_cast<MemberExpr>(E)) { + const Type* StructType = + Member->getBase()->getType().IgnoreParens().getUnqualifiedType().getDesugaredType(*Context).getTypePtr(); + if (auto* T = dyn_cast<PointerType>(StructType)) + StructType = T->getPointeeType().IgnoreParens().getUnqualifiedType().getDesugaredType(*Context).getTypePtr(); + auto* StructDecl = dyn_cast<RecordType>(StructType)->getDecl(); + std::string StructName = StructDecl->getNameAsString(); + if (StructName.empty()) { + // The struct may be anonymous, but we need some name. + // Ideally we generate the same name we generate in struct definitions, then it will be possible + // to match them between each other. However, it does not seem to be easy. We can use DeclContext::getParent + // to get declaration of the enclosing struct, but we will also need to figure out the field index + // and handle all corner cases. For now we just use the following quick hack: hash declaration file:line. + // Note: the hash must be stable across different machines (for test golden files), so we take just + // the last part of the file name. + const std::string& SourceFile = + std::filesystem::path( + SourceManager->getFilename(SourceManager->getExpansionLoc(StructDecl->getBeginLoc())).str()) + .filename() + .string(); + int Line = SourceManager->getExpansionLineNumber(StructDecl->getBeginLoc()); + StructName = std::to_string(std::hash<std::string>()(SourceFile) + std::hash<int>()(Line)); + } + return EntityField{ + .Struct = StructName, + .Field = Member->getMemberDecl()->getNameAsString(), + }; + } else if (auto* Unary = dyn_cast<UnaryOperator>(E)) { + if (Unary->getOpcode() == UnaryOperatorKind::UO_AddrOf) { + if (auto* DeclRef = dyn_cast<DeclRefExpr>(removeCasts(Unary->getSubExpr()))) { + if (auto* Var = dyn_cast<VarDecl>(DeclRef->getDecl())) { + if (Var->hasGlobalStorage()) { + return EntityGlobalAddr{ + .Name = getUniqueDeclName(Var), + }; + } + } + } + } + } else if (auto* Call = dyn_cast<CallExpr>(E)) { + if (isInterestingCall(Call)) { + return EntityReturn{ + .Func = Call->getDirectCallee()->getNameAsString(), + }; + } + } + return {}; +} + +std::optional<TypingEntity> Extractor::getDeclTypingEntity(const std::string& CurrentFunc, + std::unordered_map<const VarDecl*, int>& LocalVars, + std::unordered_map<std::string, int>& LocalSeq, + const Decl* Decl) { + if (auto* Parm = dyn_cast<ParmVarDecl>(Decl)) { + return EntityArgument{ + .Func = CurrentFunc, + .Arg = Parm->getFunctionScopeIndex(), + }; + } else if (auto* Var = dyn_cast<VarDecl>(Decl)) { + if (Var->hasLocalStorage()) { + std::string VarName = Var->getNameAsString(); + // Theoretically there can be several local vars with the same name. + // Give them unique suffixes if that's the case. + if (LocalVars.count(Var) == 0) + LocalVars[Var] = LocalSeq[VarName]++; + if (int Seq = LocalVars[Var]) + VarName += std::to_string(Seq); + return EntityLocal{ + .Name = VarName, + }; + } + } + return {}; +} + void Extractor::matchSyscall() { const auto* Func = getResult<FunctionDecl>("syscall"); std::vector<Field> Args; @@ -626,7 +775,7 @@ void Extractor::matchFileOps() { return; } const auto* Var = getResult<VarDecl>("var"); - std::string VarName = Var->getNameAsString() + "_" + getDeclFileID(Var); + std::string VarName = getUniqueDeclName(Var); int NameSeq = FileOpsDedup[VarName]++; if (NameSeq) VarName += std::to_string(NameSeq); diff --git a/tools/syz-declextract/clangtool/json.h b/tools/syz-declextract/clangtool/json.h index fbbcc12a1..873313a09 100644 --- a/tools/syz-declextract/clangtool/json.h +++ b/tools/syz-declextract/clangtool/json.h @@ -53,6 +53,7 @@ private: }; inline void print(JSONPrinter& Printer, int V) { printf("%d", V); } +inline void print(JSONPrinter& Printer, unsigned V) { printf("%u", V); } inline void print(JSONPrinter& Printer, int64_t V) { printf("%ld", V); } inline void print(JSONPrinter& Printer, bool V) { printf("%s", V ? "true" : "false"); } inline void print(JSONPrinter& Printer, const char* V) { printf("\"%s\"", V ? V : ""); } diff --git a/tools/syz-declextract/clangtool/output.h b/tools/syz-declextract/clangtool/output.h index eea86afa2..24ab82f61 100644 --- a/tools/syz-declextract/clangtool/output.h +++ b/tools/syz-declextract/clangtool/output.h @@ -108,12 +108,62 @@ struct FileOps { std::vector<IoctlCmd> IoctlCmds; }; +struct EntityReturn { + std::string Func; +}; + +struct EntityArgument { + std::string Func; + unsigned Arg; +}; + +struct EntityField { + std::string Struct; + std::string Field; +}; + +struct EntityLocal { + std::string Name; +}; + +struct EntityGlobalAddr { + std::string Name; +}; + +struct EntityResource { + std::string Type; + std::string SubType; +}; + +struct TypingEntity { + std::unique_ptr<EntityReturn> Return; + std::unique_ptr<EntityArgument> Argument; + std::unique_ptr<EntityField> Field; + std::unique_ptr<EntityLocal> Local; + std::unique_ptr<EntityGlobalAddr> GlobalAddr; + std::unique_ptr<EntityResource> Resource; + + TypingEntity() = default; + TypingEntity(EntityReturn&& E) : Return(std::make_unique<EntityReturn>(std::move(E))) {} + TypingEntity(EntityArgument&& E) : Argument(std::make_unique<EntityArgument>(std::move(E))) {} + TypingEntity(EntityField&& E) : Field(std::make_unique<EntityField>(std::move(E))) {} + TypingEntity(EntityLocal&& E) : Local(std::make_unique<EntityLocal>(std::move(E))) {} + TypingEntity(EntityGlobalAddr&& E) : GlobalAddr(std::make_unique<EntityGlobalAddr>(std::move(E))) {} + TypingEntity(EntityResource&& E) : Resource(std::make_unique<EntityResource>(std::move(E))) {} +}; + +struct TypingFact { + TypingEntity Src; + TypingEntity Dst; +}; + struct Function { std::string Name; std::string File; bool IsStatic = false; int LOC = 0; std::vector<std::string> Calls; + std::vector<TypingFact> Facts; }; struct Syscall { @@ -129,7 +179,7 @@ struct IouringOp { struct NetlinkOp { std::string Name; std::string Func; - const char* Access; + const char* Access = nullptr; std::string Policy; }; @@ -245,13 +295,69 @@ inline void print(JSONPrinter& Printer, const FileOps& V) { Printer.Field("ioctl_cmds", V.IoctlCmds, true); } +inline void print(JSONPrinter& Printer, const EntityReturn& V) { + JSONPrinter::Scope Scope(Printer); + Printer.Field("func", V.Func, true); +} + +inline void print(JSONPrinter& Printer, const EntityArgument& V) { + JSONPrinter::Scope Scope(Printer); + Printer.Field("func", V.Func); + Printer.Field("arg", V.Arg, true); +} + +inline void print(JSONPrinter& Printer, const EntityField& V) { + JSONPrinter::Scope Scope(Printer); + Printer.Field("struct", V.Struct); + Printer.Field("field", V.Field, true); +} + +inline void print(JSONPrinter& Printer, const EntityLocal& V) { + JSONPrinter::Scope Scope(Printer); + Printer.Field("name", V.Name, true); +} + +inline void print(JSONPrinter& Printer, const EntityGlobalAddr& V) { + JSONPrinter::Scope Scope(Printer); + Printer.Field("name", V.Name, true); +} + +inline void print(JSONPrinter& Printer, const EntityResource& V) { + JSONPrinter::Scope Scope(Printer); + Printer.Field("type", V.Type); + Printer.Field("subtype", V.SubType, true); +} + +inline void print(JSONPrinter& Printer, const TypingEntity& V) { + JSONPrinter::Scope Scope(Printer); + if (V.Return) + Printer.Field("return", *V.Return, true); + else if (V.Argument) + Printer.Field("argument", *V.Argument, true); + else if (V.Field) + Printer.Field("field", *V.Field, true); + else if (V.Local) + Printer.Field("local", *V.Local, true); + else if (V.GlobalAddr) + Printer.Field("global_addr", *V.GlobalAddr, true); + else + Printer.Field("resource", *V.Resource, true); +} + +inline void print(JSONPrinter& Printer, const TypingFact& V) { + JSONPrinter::Scope Scope(Printer); + Printer.Field("src", V.Src); + Printer.Field("dst", V.Dst, true); +} + inline void print(JSONPrinter& Printer, const Function& V) { JSONPrinter::Scope Scope(Printer); Printer.Field("name", V.Name); Printer.Field("file", V.File); Printer.Field("is_static", V.IsStatic); Printer.Field("loc", V.LOC); - Printer.Field("calls", V.Calls, true); + Printer.Field("calls", V.Calls); + Printer.Field("facts", V.Facts, true); } inline void print(JSONPrinter& Printer, const Syscall& V) { diff --git a/tools/syz-declextract/declextract.go b/tools/syz-declextract/declextract.go index 62008d8ad..0853756bf 100644 --- a/tools/syz-declextract/declextract.go +++ b/tools/syz-declextract/declextract.go @@ -47,10 +47,11 @@ func main() { return probe(cfg, *flagConfig) } if err := run(filepath.FromSlash("sys/linux/auto.txt"), loadProbeInfo, &clangtool.Config{ - ToolBin: *flagBinary, - KernelSrc: cfg.KernelSrc, - KernelObj: cfg.KernelObj, - CacheFile: filepath.Join(cfg.Workdir, "declextract.cache"), + ToolBin: *flagBinary, + KernelSrc: cfg.KernelSrc, + KernelObj: cfg.KernelObj, + CacheFile: filepath.Join(cfg.Workdir, "declextract.cache"), + DebugTrace: os.Stderr, }); err != nil { tool.Fail(err) } @@ -61,7 +62,7 @@ func run(autoFile string, loadProbeInfo func() (*ifaceprobe.Info, error), cfg *c if err != nil { return err } - descriptions, interfaces, err := declextract.Run(out, probeInfo, syscallRename) + descriptions, interfaces, err := declextract.Run(out, probeInfo, syscallRename, cfg.DebugTrace) if err != nil { return err } diff --git a/tools/syz-declextract/declextract_test.go b/tools/syz-declextract/declextract_test.go index 2f8dd70d5..ec1071327 100644 --- a/tools/syz-declextract/declextract_test.go +++ b/tools/syz-declextract/declextract_test.go @@ -17,6 +17,7 @@ import ( "github.com/google/syzkaller/pkg/compiler" "github.com/google/syzkaller/pkg/ifaceprobe" "github.com/google/syzkaller/pkg/osutil" + "github.com/google/syzkaller/pkg/testutil" ) var ( @@ -132,10 +133,11 @@ func testEachFile(t *testing.T, fn func(t *testing.T, cfg *clangtool.Config, fil t.Fatal(err) } cfg := &clangtool.Config{ - ToolBin: *flagBin, - KernelSrc: testdata, - KernelObj: buildDir, - CacheFile: filepath.Join(buildDir, filepath.Base(file)+".json"), + ToolBin: *flagBin, + KernelSrc: testdata, + KernelObj: buildDir, + CacheFile: filepath.Join(buildDir, filepath.Base(file)+".json"), + DebugTrace: &testutil.Writer{TB: t}, } fn(t, cfg, file) }) diff --git a/tools/syz-declextract/testdata/functions.c b/tools/syz-declextract/testdata/functions.c index 675489432..fd06fb455 100644 --- a/tools/syz-declextract/testdata/functions.c +++ b/tools/syz-declextract/testdata/functions.c @@ -11,15 +11,52 @@ static void func_bar() { func_foo(); } -void func_baz(int f) { +int alloc_fd() { + return 1; +} + +void __fget_light(int fd) { +} + +int from_kuid() { + return 1; +} + +int func_baz(int f) { func_foo(); if (f) func_bar(); if (__builtin_constant_p(f)) func_bar(); + if (f) + return from_kuid(); + return alloc_fd(); +} + +int func_qux() { + int fd = alloc_fd(); + return fd; +} + +SYSCALL_DEFINE1(functions, long x) { + __fget_light(x); + return func_baz(1); +} + +struct Typed { + int a; + int b; + int c; +}; + +int typing1(int a, int b) { + return a; } -SYSCALL_DEFINE1(functions) { - func_baz(1); - return 0; +int typing(struct Typed* t1, int i) { + struct Typed t2; + t2.a = t1->b; + int l = typing1(i, t2.a); + t1->c = l; + return l; } diff --git a/tools/syz-declextract/testdata/functions.c.info b/tools/syz-declextract/testdata/functions.c.info index 0101daf12..2e2720113 100644 --- a/tools/syz-declextract/testdata/functions.c.info +++ b/tools/syz-declextract/testdata/functions.c.info @@ -1 +1 @@ -SYSCALL functions func:__do_sys_functions loc:8 access:unknown manual_desc:false auto_desc:true file:functions.c subsystem:kernel +SYSCALL functions func:__do_sys_functions loc:13 access:unknown manual_desc:false auto_desc:true file:functions.c subsystem:kernel diff --git a/tools/syz-declextract/testdata/functions.c.json b/tools/syz-declextract/testdata/functions.c.json index 8a1fd4ee1..eb1b3b880 100644 --- a/tools/syz-declextract/testdata/functions.c.json +++ b/tools/syz-declextract/testdata/functions.c.json @@ -5,10 +5,48 @@ "file": "functions.c", "loc": 2, "calls": [ + "__fget_light", "func_baz" + ], + "facts": [ + { + "src": { + "argument": { + "func": "__do_sys_functions", + "arg": 0 + } + }, + "dst": { + "argument": { + "func": "__fget_light", + "arg": 0 + } + } + }, + { + "src": { + "return": { + "func": "func_baz" + } + }, + "dst": { + "return": { + "func": "__do_sys_functions" + } + } + } ] }, { + "name": "__fget_light", + "file": "functions.c" + }, + { + "name": "alloc_fd", + "file": "functions.c", + "loc": 1 + }, + { "name": "atomic_load32", "file": "include/types.h", "is_static": true, @@ -20,6 +58,11 @@ "loc": 1 }, { + "name": "from_kuid", + "file": "functions.c", + "loc": 1 + }, + { "name": "func_bar", "file": "functions.c", "is_static": true, @@ -31,21 +74,205 @@ { "name": "func_baz", "file": "functions.c", - "loc": 5, + "loc": 8, "calls": [ "func_foo", - "func_bar" + "func_bar", + "from_kuid", + "alloc_fd" + ], + "facts": [ + { + "src": { + "return": { + "func": "from_kuid" + } + }, + "dst": { + "return": { + "func": "func_baz" + } + } + }, + { + "src": { + "return": { + "func": "alloc_fd" + } + }, + "dst": { + "return": { + "func": "func_baz" + } + } + } ] }, { "name": "func_foo", "file": "functions.c", "is_static": true + }, + { + "name": "func_qux", + "file": "functions.c", + "loc": 2, + "calls": [ + "alloc_fd" + ], + "facts": [ + { + "src": { + "return": { + "func": "alloc_fd" + } + }, + "dst": { + "local": { + "name": "fd" + } + } + }, + { + "src": { + "local": { + "name": "fd" + } + }, + "dst": { + "return": { + "func": "func_qux" + } + } + } + ] + }, + { + "name": "typing", + "file": "functions.c", + "loc": 5, + "calls": [ + "typing1" + ], + "facts": [ + { + "src": { + "argument": { + "func": "typing", + "arg": 1 + } + }, + "dst": { + "argument": { + "func": "typing1", + "arg": 0 + } + } + }, + { + "src": { + "field": { + "struct": "Typed", + "field": "a" + } + }, + "dst": { + "argument": { + "func": "typing1", + "arg": 1 + } + } + }, + { + "src": { + "field": { + "struct": "Typed", + "field": "b" + } + }, + "dst": { + "field": { + "struct": "Typed", + "field": "a" + } + } + }, + { + "src": { + "local": { + "name": "l" + } + }, + "dst": { + "field": { + "struct": "Typed", + "field": "c" + } + } + }, + { + "src": { + "return": { + "func": "typing1" + } + }, + "dst": { + "local": { + "name": "l" + } + } + }, + { + "src": { + "local": { + "name": "l" + } + }, + "dst": { + "return": { + "func": "typing" + } + } + } + ] + }, + { + "name": "typing1", + "file": "functions.c", + "loc": 1, + "facts": [ + { + "src": { + "argument": { + "func": "typing1", + "arg": 0 + } + }, + "dst": { + "return": { + "func": "typing1" + } + } + } + ] } ], "syscalls": [ { "func": "__do_sys_functions", + "args": [ + { + "name": "x", + "counted_by": -1, + "type": { + "int": { + "byte_size": 8, + "name": "long", + "base": "long" + } + } + } + ], "source_file": "functions.c" } ] diff --git a/tools/syz-declextract/testdata/functions.c.txt b/tools/syz-declextract/testdata/functions.c.txt index cab813f8b..6dc51303b 100644 --- a/tools/syz-declextract/testdata/functions.c.txt +++ b/tools/syz-declextract/testdata/functions.c.txt @@ -8,4 +8,4 @@ include <vdso/bits.h> include <linux/types.h> include <net/netlink.h> -functions$auto() +functions$auto(x fd) fd diff --git a/tools/syz-declextract/testdata/types.c b/tools/syz-declextract/testdata/types.c index 8fc67aeb9..20f92673f 100644 --- a/tools/syz-declextract/testdata/types.c +++ b/tools/syz-declextract/testdata/types.c @@ -53,3 +53,14 @@ SYSCALL_DEFINE1(types_syscall, struct anon_struct* p, struct empty_struct* y, struct bitfields* b, int pid, fd_t f, struct various* v) { return 0; } + +void anon_flow(int x) { + struct anon_struct s; + s.a.x = x; + s.y = x; + s.w = x; + s.foo.f = x; + s.array[1].a = x; + s.ptr->a = x; + s.ptr_array[1]->b = x; +} diff --git a/tools/syz-declextract/testdata/types.c.json b/tools/syz-declextract/testdata/types.c.json index 9733798ab..a5a7088db 100644 --- a/tools/syz-declextract/testdata/types.c.json +++ b/tools/syz-declextract/testdata/types.c.json @@ -4,6 +4,111 @@ "name": "__do_sys_types_syscall", "file": "types.c", "loc": 2 + }, + { + "name": "anon_flow", + "file": "types.c", + "loc": 8, + "facts": [ + { + "src": { + "argument": { + "func": "anon_flow", + "arg": 0 + } + }, + "dst": { + "field": { + "struct": "11253655576479126316", + "field": "x" + } + } + }, + { + "src": { + "argument": { + "func": "anon_flow", + "arg": 0 + } + }, + "dst": { + "field": { + "struct": "11253655576479126318", + "field": "y" + } + } + }, + { + "src": { + "argument": { + "func": "anon_flow", + "arg": 0 + } + }, + "dst": { + "field": { + "struct": "11253655576479126319", + "field": "w" + } + } + }, + { + "src": { + "argument": { + "func": "anon_flow", + "arg": 0 + } + }, + "dst": { + "field": { + "struct": "11253655576479126309", + "field": "f" + } + } + }, + { + "src": { + "argument": { + "func": "anon_flow", + "arg": 0 + } + }, + "dst": { + "field": { + "struct": "11253655576479126322", + "field": "a" + } + } + }, + { + "src": { + "argument": { + "func": "anon_flow", + "arg": 0 + } + }, + "dst": { + "field": { + "struct": "11253655576479126323", + "field": "a" + } + } + }, + { + "src": { + "argument": { + "func": "anon_flow", + "arg": 0 + } + }, + "dst": { + "field": { + "struct": "11253655576479126324", + "field": "b" + } + } + } + ] } ], "defines": [ |
