aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2024-12-13 15:15:49 +0100
committerDmitry Vyukov <dvyukov@google.com>2024-12-17 13:44:24 +0000
commitc8c15bb214509bafc8fe1a1e3abb8ccf90b3306e (patch)
treeca722a71aff5a1566389f178d9c95d7d7e8caeed /tools
parentbc1a1b50f942408a9139887b914f745d9fa02adc (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.cpp189
-rw-r--r--tools/syz-declextract/clangtool/json.h1
-rw-r--r--tools/syz-declextract/clangtool/output.h110
-rw-r--r--tools/syz-declextract/declextract.go11
-rw-r--r--tools/syz-declextract/declextract_test.go10
-rw-r--r--tools/syz-declextract/testdata/functions.c45
-rw-r--r--tools/syz-declextract/testdata/functions.c.info2
-rw-r--r--tools/syz-declextract/testdata/functions.c.json231
-rw-r--r--tools/syz-declextract/testdata/functions.c.txt2
-rw-r--r--tools/syz-declextract/testdata/types.c11
-rw-r--r--tools/syz-declextract/testdata/types.c.json105
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": [