aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2025-01-17 10:39:49 +0100
committerDmitry Vyukov <dvyukov@google.com>2025-01-22 17:12:18 +0000
commit8aaf5d60aa0b3ddb05e117f52c0e30ec246b7aad (patch)
tree63ddc4520d1e4b865925a014d3401b5e15c1fed3 /tools
parentac680c7cc91ea82316471433537f3101c2af39ea (diff)
tools/syz-declextract: support function scopes
Extract info about function scopes formed by switch'es on function arguments. For example if we have: void foo(..., int cmd, ...) { ... switch (cmd) { case FOO: ... block 1 ... case BAR: ... block 2 ... } ... } We record that any data flow within block 1 is only relevant when foo's arg cmd has value FOO, similarly for block 2 and BAR. This allows to do 3 things: 1. Locate ioctl commands that are switched on within transitively called functions. 2. Infer return value for each ioctl command. 3. Infer argument type when it's not specified in _IO macro. This will also allow to infer other multiplexed syscalls. Descriptions generated on Linux commit c4b9570cfb63501.
Diffstat (limited to 'tools')
-rw-r--r--tools/syz-declextract/clangtool/declextract.cpp324
-rw-r--r--tools/syz-declextract/clangtool/output.h36
-rw-r--r--tools/syz-declextract/testdata/arch/x86/syscalls.tbl2
-rw-r--r--tools/syz-declextract/testdata/file_operations.c15
-rw-r--r--tools/syz-declextract/testdata/file_operations.c.json313
-rw-r--r--tools/syz-declextract/testdata/file_operations.c.txt3
-rw-r--r--tools/syz-declextract/testdata/functions.c.json433
-rw-r--r--tools/syz-declextract/testdata/include/fs.h7
-rw-r--r--tools/syz-declextract/testdata/include/uapi/file_operations.h4
-rw-r--r--tools/syz-declextract/testdata/io_uring.c.json49
-rw-r--r--tools/syz-declextract/testdata/netlink.c.json49
-rw-r--r--tools/syz-declextract/testdata/scopes.c30
-rw-r--r--tools/syz-declextract/testdata/scopes.c.info1
-rw-r--r--tools/syz-declextract/testdata/scopes.c.json286
-rw-r--r--tools/syz-declextract/testdata/scopes.c.txt7
-rw-r--r--tools/syz-declextract/testdata/syscall.c.json14
-rw-r--r--tools/syz-declextract/testdata/types.c.info2
-rw-r--r--tools/syz-declextract/testdata/types.c.json215
18 files changed, 1283 insertions, 507 deletions
diff --git a/tools/syz-declextract/clangtool/declextract.cpp b/tools/syz-declextract/clangtool/declextract.cpp
index d7d6bc824..98d2342e8 100644
--- a/tools/syz-declextract/clangtool/declextract.cpp
+++ b/tools/syz-declextract/clangtool/declextract.cpp
@@ -12,6 +12,7 @@
#include "clang/AST/DeclarationName.h"
#include "clang/AST/Expr.h"
#include "clang/AST/PrettyPrinter.h"
+#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/Type.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
@@ -32,8 +33,10 @@
#include <cstddef>
#include <cstdint>
#include <filesystem>
+#include <stack>
#include <string>
#include <string_view>
+#include <tuple>
#include <unordered_map>
#include <vector>
@@ -49,6 +52,13 @@ struct MacroDef {
};
using MacroMap = std::unordered_map<std::string, MacroDef>;
+struct MacroDesc {
+ std::string Name;
+ std::string Value;
+ SourceRange SourceRange;
+ int64_t IntValue;
+};
+
class Extractor : public MatchFinder, public tooling::SourceFileCallbacks {
public:
Extractor() {
@@ -80,6 +90,7 @@ public:
void print() const { Output.print(); }
private:
+ friend struct FunctionAnalyzer;
using MatchFunc = void (Extractor::*)();
// Thunk that redirects MatchCallback::run method to one of the methods of the Extractor class.
struct MatchCallbackThunk : MatchFinder::MatchCallback {
@@ -129,13 +140,9 @@ private:
std::optional<QualType> getSizeofType(const Expr* E);
int sizeofType(const Type* T);
int alignofType(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);
+ void extractIoctl(const Expr* Cmd, const MacroDesc& Macro);
+ int getStmtLOC(const Stmt* S);
+ std::optional<MacroDesc> isMacroRef(const Expr* E);
};
// PPCallbacksTracker records all macro definitions (name/value/source location).
@@ -343,6 +350,29 @@ std::string Extractor::getDeclFileID(const Decl* Decl) {
return file;
}
+int Extractor::getStmtLOC(const Stmt* S) {
+ return std::max<int>(0, SourceManager->getExpansionLineNumber(S->getSourceRange().getEnd()) -
+ SourceManager->getExpansionLineNumber(S->getSourceRange().getBegin()) - 1);
+}
+
+std::optional<MacroDesc> Extractor::isMacroRef(const Expr* E) {
+ if (!E)
+ return {};
+ auto Range = Lexer::getAsCharRange(E->getSourceRange(), *SourceManager, Context->getLangOpts());
+ const std::string& Str = Lexer::getSourceText(Range, *SourceManager, Context->getLangOpts()).str();
+ auto MacroDef = Macros.find(Str);
+ if (MacroDef == Macros.end())
+ return {};
+ int64_t Val = evaluate(E);
+ emitConst(Str, Val, MacroDef->second.SourceRange.getBegin());
+ return MacroDesc{
+ .Name = Str,
+ .Value = MacroDef->second.Value,
+ .SourceRange = MacroDef->second.SourceRange,
+ .IntValue = Val,
+ };
+}
+
template <typename Node> void matchHelper(MatchFinder& Finder, ASTContext* Context, const Node* Expr) {
Finder.match(*Expr, *Context);
}
@@ -587,78 +617,179 @@ bool isInterestingCall(const CallExpr* Call) {
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 (!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,
- }});
+struct FunctionAnalyzer : RecursiveASTVisitor<FunctionAnalyzer> {
+ FunctionAnalyzer(Extractor* Extractor, const FunctionDecl* Func)
+ : Extractor(Extractor), CurrentFunc(Func->getNameAsString()), Context(Extractor->Context),
+ SourceManager(Extractor->SourceManager) {
+ // The global function scope.
+ Scopes.push_back(FunctionScope{.Arg = -1, .LOC = Extractor->getStmtLOC(Func->getBody())});
+ Current = &Scopes[0];
+ TraverseStmt(Func->getBody());
+ }
+
+ bool VisitBinaryOperator(const BinaryOperator* B) {
+ if (B->isAssignmentOp())
+ noteFact(getTypingEntity(B->getRHS()), getTypingEntity(B->getLHS()));
+ return true;
+ }
+
+ bool VisitVarDecl(const VarDecl* D) {
+ if (D->getStorageDuration() == SD_Automatic)
+ noteFact(getTypingEntity(D->getInit()), getDeclTypingEntity(D));
+ return true;
+ }
+
+ bool VisitReturnStmt(const ReturnStmt* Ret) {
+ noteFact(getTypingEntity(Ret->getRetValue()), EntityReturn{.Func = CurrentFunc});
+ return true;
+ }
+
+ bool VisitCallExpr(const CallExpr* Call) {
+ if (isInterestingCall(Call)) {
+ const std::string& Callee = Call->getDirectCallee()->getNameAsString();
+ Current->Calls.push_back(Callee);
+ for (unsigned AI = 0; AI < Call->getNumArgs(); AI++) {
+ noteFact(getTypingEntity(Call->getArg(AI)), EntityArgument{
+ .Func = Callee,
+ .Arg = AI,
+ });
}
}
- if (!CalleesDedup.insert(Callee).second)
- continue;
- Callees.push_back(Callee);
+ return true;
}
- 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)});
+
+ bool VisitSwitchStmt(const SwitchStmt* S) {
+ // We are only interested in switches on the function arguments
+ // with cases that mention defines from uapi headers.
+ // This covers ioctl/fcntl/prctl/ptrace/etc.
+ bool IsInteresting = false;
+ auto Param = getTypingEntity(S->getCond());
+ if (Current == &Scopes[0] && Param && Param->Argument) {
+ for (auto* C = S->getSwitchCaseList(); C; C = C->getNextSwitchCase()) {
+ auto* Case = dyn_cast<CaseStmt>(C);
+ if (!Case)
+ continue;
+ auto LMacro = Extractor->isMacroRef(Case->getLHS());
+ auto RMacro = Extractor->isMacroRef(Case->getRHS());
+ if (LMacro || RMacro) {
+ IsInteresting = true;
+ break;
+ }
+ }
+ }
+
+ int Begin = SourceManager->getExpansionLineNumber(S->getBeginLoc());
+ int End = SourceManager->getExpansionLineNumber(S->getEndLoc());
+ if (IsInteresting)
+ Scopes[0].LOC = std::max<int>(0, Scopes[0].LOC - (End - Begin));
+ SwitchStack.push({S, IsInteresting, IsInteresting ? static_cast<int>(Param->Argument->Arg) : -1, End});
+ return true;
}
- 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,
- }});
+
+ bool VisitSwitchCase(const SwitchCase* C) {
+ if (!SwitchStack.top().IsInteresting)
+ return true;
+ // If there are several cases with the same "body", we want to create new scope
+ // only for the first one:
+ // case FOO:
+ // case BAR:
+ // ... some code ...
+ if (!C->getNextSwitchCase() || C->getNextSwitchCase()->getSubStmt() != C) {
+ int Line = SourceManager->getExpansionLineNumber(C->getBeginLoc());
+ if (Current != &Scopes[0])
+ Current->LOC = Line - Current->LOC;
+ Scopes.push_back(FunctionScope{
+ .Arg = SwitchStack.top().Arg,
+ .LOC = Line,
+ });
+ Current = &Scopes.back();
+ }
+ // Otherwise it's a default case, for which we don't add any values.
+ if (auto* Case = dyn_cast<CaseStmt>(C)) {
+ int64_t LVal = Extractor->evaluate(Case->getLHS());
+ auto LMacro = Extractor->isMacroRef(Case->getLHS());
+ if (LMacro) {
+ Current->Values.push_back(LMacro->Name);
+ Extractor->extractIoctl(Case->getLHS(), *LMacro);
+ } else {
+ Current->Values.push_back(std::to_string(LVal));
+ }
+ if (Case->caseStmtIsGNURange()) {
+ // GNU range is:
+ // case FOO ... BAR:
+ // Add all values in the range.
+ int64_t RVal = Extractor->evaluate(Case->getRHS());
+ auto RMacro = Extractor->isMacroRef(Case->getRHS());
+ for (int64_t V = LVal + 1; V <= RVal - (RMacro ? 1 : 0); V++)
+ Current->Values.push_back(std::to_string(V));
+ if (RMacro)
+ Current->Values.push_back(RMacro->Name);
+ }
}
+ return true;
+ }
+
+ bool dataTraverseStmtPost(const Stmt* S) {
+ if (SwitchStack.empty())
+ return true;
+ auto Top = SwitchStack.top();
+ if (Top.S != S)
+ return true;
+ if (Top.IsInteresting) {
+ Current->LOC = Top.EndLine - Current->LOC;
+ Current = &Scopes[0];
+ }
+ SwitchStack.pop();
+ return true;
+ }
+
+ void noteFact(std::optional<TypingEntity>&& Src, std::optional<TypingEntity>&& Dst) {
+ if (Src && Dst)
+ Current->Facts.push_back({std::move(*Src), std::move(*Dst)});
}
+
+ std::optional<TypingEntity> getTypingEntity(const Expr* E);
+ std::optional<TypingEntity> getDeclTypingEntity(const Decl* Decl);
+
+ struct SwitchDesc {
+ const SwitchStmt* S;
+ bool IsInteresting;
+ int Arg;
+ int EndLine;
+ };
+
+ Extractor* Extractor;
+ std::string CurrentFunc;
+ ASTContext* Context;
+ SourceManager* SourceManager;
+ std::vector<FunctionScope> Scopes;
+ FunctionScope* Current = nullptr;
+ std::unordered_map<const VarDecl*, int> LocalVars;
+ std::unordered_map<std::string, int> LocalSeq;
+ std::stack<SwitchDesc> SwitchStack;
+};
+
+void Extractor::matchFunctionDef() {
+ const auto* Func = getResult<FunctionDecl>("function");
+ if (!Func->getBody())
+ return;
+ const std::string& SourceFile = std::filesystem::relative(
+ SourceManager->getFilename(SourceManager->getExpansionLoc(Func->getSourceRange().getBegin())).str());
+ FunctionAnalyzer Analyzer(this, Func);
Output.emit(Function{
- .Name = CurrentFunc,
+ .Name = Func->getNameAsString(),
.File = SourceFile,
.IsStatic = Func->isStatic(),
- .LOC = LOC,
- .Calls = std::move(Callees),
- .Facts = std::move(Facts),
+ .Scopes = std::move(Analyzer.Scopes),
});
}
-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) {
+std::optional<TypingEntity> FunctionAnalyzer::getTypingEntity(const Expr* E) {
if (!E)
return {};
E = removeCasts(E);
if (auto* DeclRef = dyn_cast<DeclRefExpr>(E)) {
- return getDeclTypingEntity(CurrentFunc, LocalVars, LocalSeq, DeclRef->getDecl());
+ return getDeclTypingEntity(DeclRef->getDecl());
} else if (auto* Member = dyn_cast<MemberExpr>(E)) {
const Type* StructType =
Member->getBase()->getType().IgnoreParens().getUnqualifiedType().getDesugaredType(*Context).getTypePtr();
@@ -692,7 +823,7 @@ std::optional<TypingEntity> Extractor::getTypingEntity(const std::string& Curren
if (auto* Var = dyn_cast<VarDecl>(DeclRef->getDecl())) {
if (Var->hasGlobalStorage()) {
return EntityGlobalAddr{
- .Name = getUniqueDeclName(Var),
+ .Name = Extractor->getUniqueDeclName(Var),
};
}
}
@@ -708,10 +839,7 @@ std::optional<TypingEntity> Extractor::getTypingEntity(const std::string& Curren
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) {
+std::optional<TypingEntity> FunctionAnalyzer::getDeclTypingEntity(const Decl* Decl) {
if (auto* Parm = dyn_cast<ParmVarDecl>(Decl)) {
return EntityArgument{
.Func = CurrentFunc,
@@ -790,7 +918,6 @@ void Extractor::matchFileOps() {
std::string Mmap = getDeclName(Fops->getInit(Fields["mmap"]));
if (Mmap.empty())
Mmap = getDeclName(Fops->getInit(Fields["get_unmapped_area"]));
- auto Cmds = extractIoctlCommands(Ioctl);
Output.emit(FileOps{
.Name = VarName,
.Open = std::move(Open),
@@ -798,47 +925,32 @@ void Extractor::matchFileOps() {
.Write = std::move(Write),
.Mmap = std::move(Mmap),
.Ioctl = std::move(Ioctl),
- .IoctlCmds = std::move(Cmds),
});
}
-std::vector<IoctlCmd> Extractor::extractIoctlCommands(const std::string& Ioctl) {
- if (Ioctl.empty())
- return {};
- // If we see the ioctl function definition, match cases of switches (very best-effort for now).
- const auto& Cases = findAllMatches<CaseStmt>(
- Context, functionDecl(hasName(Ioctl), forEachDescendant(switchStmt(forEachSwitchCase(caseStmt().bind("res"))))));
- std::vector<IoctlCmd> Results;
- for (auto* Case : Cases) {
- const auto* Cmd = Case->getLHS();
- auto Range = Lexer::getAsCharRange(Cmd->getSourceRange(), *SourceManager, Context->getLangOpts());
- std::string CmdStr = Lexer::getSourceText(Range, *SourceManager, Context->getLangOpts()).str();
- auto MacroDef = Macros.find(CmdStr);
- if (MacroDef == Macros.end())
- continue;
- int64_t CmdVal = evaluate(Cmd);
- emitConst(CmdStr, CmdVal, MacroDef->second.SourceRange.getBegin());
- FieldType CmdType;
- const auto Dir = _IOC_DIR(CmdVal);
- if (Dir == _IOC_NONE) {
- CmdType = IntType{.ByteSize = 1, .IsConst = true};
- } else if (std::optional<QualType> Arg = getSizeofType(Cmd)) {
- CmdType = PtrType{
- .Elem = genType(*Arg),
- .IsConst = Dir == _IOC_READ,
- };
- } else {
- CmdType = PtrType{
- .Elem = BufferType{},
- .IsConst = Dir == _IOC_READ,
- };
- }
- Results.push_back(IoctlCmd{
- .Name = CmdStr,
- .Type = std::move(CmdType),
- });
+void Extractor::extractIoctl(const Expr* Cmd, const MacroDesc& Macro) {
+ // This is old style ioctl defined directly via a number.
+ // We can't infer anything about it.
+ if (Macro.Value.find("_IO") != 0)
+ return;
+ FieldType Type;
+ auto Dir = _IOC_DIR(Macro.IntValue);
+ if (Dir == _IOC_NONE) {
+ Type = IntType{.ByteSize = 1, .IsConst = true};
+ } else if (std::optional<QualType> Arg = getSizeofType(Cmd)) {
+ Type = PtrType{
+ .Elem = genType(*Arg),
+ .IsConst = Dir == _IOC_READ,
+ };
+ } else {
+ // It is an ioctl, but we failed to get the arg type.
+ // Let the Go part figure out a good arg type.
+ return;
}
- return Results;
+ Output.emit(Ioctl{
+ .Name = Macro.Name,
+ .Type = std::move(Type),
+ });
}
int main(int argc, const char** argv) {
diff --git a/tools/syz-declextract/clangtool/output.h b/tools/syz-declextract/clangtool/output.h
index ffa0844c9..4a482ed3c 100644
--- a/tools/syz-declextract/clangtool/output.h
+++ b/tools/syz-declextract/clangtool/output.h
@@ -93,7 +93,7 @@ struct Enum {
std::vector<std::string> Values;
};
-struct IoctlCmd {
+struct Ioctl {
std::string Name;
FieldType Type;
};
@@ -105,7 +105,6 @@ struct FileOps {
std::string Write;
std::string Mmap;
std::string Ioctl;
- std::vector<IoctlCmd> IoctlCmds;
};
struct EntityReturn {
@@ -157,13 +156,19 @@ struct TypingFact {
TypingEntity Dst;
};
+struct FunctionScope {
+ int Arg = 0;
+ int LOC = 0;
+ std::vector<std::string> Values;
+ std::vector<std::string> Calls;
+ std::vector<TypingFact> Facts;
+};
+
struct Function {
std::string Name;
std::string File;
bool IsStatic = false;
- int LOC = 0;
- std::vector<std::string> Calls;
- std::vector<TypingFact> Facts;
+ std::vector<FunctionScope> Scopes;
};
struct Syscall {
@@ -282,7 +287,7 @@ inline void print(JSONPrinter& Printer, const BufferType& V) {
Printer.Field("is_non_terminated", V.IsNonTerminated, true);
}
-inline void print(JSONPrinter& Printer, const IoctlCmd& V) {
+inline void print(JSONPrinter& Printer, const Ioctl& V) {
JSONPrinter::Scope Scope(Printer);
Printer.Field("name", V.Name);
Printer.Field("type", V.Type, true);
@@ -295,8 +300,7 @@ inline void print(JSONPrinter& Printer, const FileOps& V) {
Printer.Field("read", V.Read);
Printer.Field("write", V.Write);
Printer.Field("mmap", V.Mmap);
- Printer.Field("ioctl", V.Ioctl);
- Printer.Field("ioctl_cmds", V.IoctlCmds, true);
+ Printer.Field("ioctl", V.Ioctl, true);
}
inline void print(JSONPrinter& Printer, const EntityReturn& V) {
@@ -354,14 +358,21 @@ inline void print(JSONPrinter& Printer, const TypingFact& V) {
Printer.Field("dst", V.Dst, true);
}
+inline void print(JSONPrinter& Printer, const FunctionScope& V) {
+ JSONPrinter::Scope Scope(Printer);
+ Printer.Field("arg", V.Arg);
+ Printer.Field("values", V.Values);
+ Printer.Field("loc", V.LOC);
+ Printer.Field("calls", V.Calls);
+ Printer.Field("facts", V.Facts, 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);
- Printer.Field("facts", V.Facts, true);
+ Printer.Field("scopes", V.Scopes, true);
}
inline void print(JSONPrinter& Printer, const Syscall& V) {
@@ -422,6 +433,7 @@ public:
void emit(Enum&& V) { Enums.push_back(std::move(V)); }
void emit(Syscall&& V) { Syscalls.push_back(std::move(V)); }
void emit(FileOps&& V) { FileOps.push_back(std::move(V)); }
+ void emit(Ioctl&& V) { Ioctls.push_back(std::move(V)); }
void emit(IouringOp&& V) { IouringOps.push_back(std::move(V)); }
void emit(NetlinkFamily&& V) { NetlinkFamilies.push_back(std::move(V)); }
void emit(NetlinkPolicy&& V) { NetlinkPolicies.push_back(std::move(V)); }
@@ -434,6 +446,7 @@ public:
Printer.Field("structs", Structs);
Printer.Field("syscalls", Syscalls);
Printer.Field("file_ops", FileOps);
+ Printer.Field("ioctls", Ioctls);
Printer.Field("iouring_ops", IouringOps);
Printer.Field("netlink_families", NetlinkFamilies);
Printer.Field("netlink_policies", NetlinkPolicies, true);
@@ -446,6 +459,7 @@ private:
std::vector<Struct> Structs;
std::vector<Syscall> Syscalls;
std::vector<FileOps> FileOps;
+ std::vector<Ioctl> Ioctls;
std::vector<IouringOp> IouringOps;
std::vector<NetlinkFamily> NetlinkFamilies;
std::vector<NetlinkPolicy> NetlinkPolicies;
diff --git a/tools/syz-declextract/testdata/arch/x86/syscalls.tbl b/tools/syz-declextract/testdata/arch/x86/syscalls.tbl
index 7f728f3f2..605db1316 100644
--- a/tools/syz-declextract/testdata/arch/x86/syscalls.tbl
+++ b/tools/syz-declextract/testdata/arch/x86/syscalls.tbl
@@ -6,3 +6,5 @@
3 64 types_syscall sys_types_syscall
4 64 align_syscall sys_align_syscall
5 64 functions sys_functions
+6 64 scopes0 sys_scopes0
+
diff --git a/tools/syz-declextract/testdata/file_operations.c b/tools/syz-declextract/testdata/file_operations.c
index 0dd8b9b21..136e608dd 100644
--- a/tools/syz-declextract/testdata/file_operations.c
+++ b/tools/syz-declextract/testdata/file_operations.c
@@ -10,7 +10,15 @@ static void foo_read() {}
static void foo_write() {}
static void foo_mmap() {}
-static void foo_ioctl(unsigned int cmd) {
+static void foo_ioctl2(unsigned int cmd, unsigned long arg) {
+ switch (cmd) {
+ case FOO_IOCTL6:
+ case FOO_IOCTL7:
+ default:
+ }
+}
+
+static void foo_ioctl(void* file, unsigned int cmd, unsigned long arg) {
switch (cmd) {
case FOO_IOCTL1:
case FOO_IOCTL2:
@@ -18,6 +26,7 @@ static void foo_ioctl(unsigned int cmd) {
case FOO_IOCTL4:
case FOO_IOCTL5:
}
+ foo_ioctl2(cmd, arg);
}
const struct file_operations foo = {
@@ -31,7 +40,7 @@ const struct file_operations foo = {
static void proc_open() {}
static void proc_read() {}
static void proc_write() {}
-static void proc_ioctl(unsigned int cmd) {}
+static void proc_ioctl(void* file, unsigned int cmd, unsigned long arg) {}
const struct file_operations proc_ops[] = {
{
@@ -47,7 +56,7 @@ const struct file_operations proc_ops[] = {
#define UNUSED_IOCTL2 _IO('c', 2)
-static void unused_ioctl(unsigned int cmd) {
+static void unused_ioctl(void* file, unsigned int cmd, unsigned long arg) {
switch (cmd) {
case UNUSED_IOCTL1:
case UNUSED_IOCTL2:
diff --git a/tools/syz-declextract/testdata/file_operations.c.json b/tools/syz-declextract/testdata/file_operations.c.json
index c8fd5a9dc..e7dfd31f6 100644
--- a/tools/syz-declextract/testdata/file_operations.c.json
+++ b/tools/syz-declextract/testdata/file_operations.c.json
@@ -4,53 +4,174 @@
"name": "foo_ioctl",
"file": "file_operations.c",
"is_static": true,
- "loc": 7
+ "scopes": [
+ {
+ "arg": -1,
+ "loc": 2,
+ "calls": [
+ "foo_ioctl2"
+ ],
+ "facts": [
+ {
+ "src": {
+ "argument": {
+ "func": "foo_ioctl",
+ "arg": 1
+ }
+ },
+ "dst": {
+ "argument": {
+ "func": "foo_ioctl2",
+ "arg": 0
+ }
+ }
+ },
+ {
+ "src": {
+ "argument": {
+ "func": "foo_ioctl",
+ "arg": 2
+ }
+ },
+ "dst": {
+ "argument": {
+ "func": "foo_ioctl2",
+ "arg": 1
+ }
+ }
+ }
+ ]
+ },
+ {
+ "arg": 1,
+ "values": [
+ "FOO_IOCTL1",
+ "FOO_IOCTL2",
+ "FOO_IOCTL3",
+ "FOO_IOCTL4",
+ "FOO_IOCTL5"
+ ],
+ "loc": 5
+ }
+ ]
+ },
+ {
+ "name": "foo_ioctl2",
+ "file": "file_operations.c",
+ "is_static": true,
+ "scopes": [
+ {
+ "arg": -1,
+ "loc": 1
+ },
+ {
+ "arg": 0,
+ "values": [
+ "FOO_IOCTL6",
+ "FOO_IOCTL7"
+ ],
+ "loc": 3
+ }
+ ]
},
{
"name": "foo_mmap",
"file": "file_operations.c",
- "is_static": true
+ "is_static": true,
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "foo_open",
"file": "file_operations.c",
- "is_static": true
+ "is_static": true,
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "foo_read",
"file": "file_operations.c",
- "is_static": true
+ "is_static": true,
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "foo_write",
"file": "file_operations.c",
- "is_static": true
+ "is_static": true,
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "proc_ioctl",
"file": "file_operations.c",
- "is_static": true
+ "is_static": true,
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "proc_open",
"file": "file_operations.c",
- "is_static": true
+ "is_static": true,
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "proc_read",
"file": "file_operations.c",
- "is_static": true
+ "is_static": true,
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "proc_write",
"file": "file_operations.c",
- "is_static": true
+ "is_static": true,
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "unused_ioctl",
"file": "file_operations.c",
"is_static": true,
- "loc": 4
+ "scopes": [
+ {
+ "arg": -1,
+ "loc": 1
+ },
+ {
+ "arg": 1,
+ "values": [
+ "UNUSED_IOCTL1",
+ "UNUSED_IOCTL2"
+ ],
+ "loc": 2
+ }
+ ]
}
],
"consts": [
@@ -80,6 +201,16 @@
"value": 3221775109
},
{
+ "name": "FOO_IOCTL6",
+ "filename": "include/uapi/file_operations.h",
+ "value": 25350
+ },
+ {
+ "name": "FOO_IOCTL7",
+ "filename": "include/uapi/file_operations.h",
+ "value": 25351
+ },
+ {
"name": "UNUSED_IOCTL1",
"filename": "include/uapi/unused_ioctl.h",
"value": 25345
@@ -129,63 +260,6 @@
"write": "foo_write",
"mmap": "foo_mmap",
"ioctl": "foo_ioctl",
- "ioctl_cmds": [
- {
- "name": "FOO_IOCTL5",
- "type": {
- "ptr": {
- "elem": {
- "struct": "foo_ioctl_arg"
- }
- }
- }
- },
- {
- "name": "FOO_IOCTL4",
- "type": {
- "ptr": {
- "elem": {
- "struct": "foo_ioctl_arg"
- }
- }
- }
- },
- {
- "name": "FOO_IOCTL3",
- "type": {
- "ptr": {
- "elem": {
- "struct": "foo_ioctl_arg"
- },
- "is_const": true
- }
- }
- },
- {
- "name": "FOO_IOCTL2",
- "type": {
- "ptr": {
- "elem": {
- "int": {
- "byte_size": 4,
- "name": "int",
- "base": "int"
- }
- },
- "is_const": true
- }
- }
- },
- {
- "name": "FOO_IOCTL1",
- "type": {
- "int": {
- "byte_size": 1,
- "is_const": true
- }
- }
- }
- ],
"source_file": "file_operations.c"
},
{
@@ -206,27 +280,100 @@
{
"name": "unused_file_operations",
"ioctl": "unused_ioctl",
- "ioctl_cmds": [
- {
- "name": "UNUSED_IOCTL2",
- "type": {
+ "source_file": "file_operations.c"
+ }
+ ],
+ "ioctls": [
+ {
+ "name": "FOO_IOCTL1",
+ "type": {
+ "int": {
+ "byte_size": 1,
+ "is_const": true
+ }
+ }
+ },
+ {
+ "name": "FOO_IOCTL2",
+ "type": {
+ "ptr": {
+ "elem": {
"int": {
- "byte_size": 1,
- "is_const": true
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
}
+ },
+ "is_const": true
+ }
+ }
+ },
+ {
+ "name": "FOO_IOCTL3",
+ "type": {
+ "ptr": {
+ "elem": {
+ "struct": "foo_ioctl_arg"
+ },
+ "is_const": true
+ }
+ }
+ },
+ {
+ "name": "FOO_IOCTL4",
+ "type": {
+ "ptr": {
+ "elem": {
+ "struct": "foo_ioctl_arg"
}
- },
- {
- "name": "UNUSED_IOCTL1",
- "type": {
- "int": {
- "byte_size": 1,
- "is_const": true
- }
+ }
+ }
+ },
+ {
+ "name": "FOO_IOCTL5",
+ "type": {
+ "ptr": {
+ "elem": {
+ "struct": "foo_ioctl_arg"
}
}
- ],
- "source_file": "file_operations.c"
+ }
+ },
+ {
+ "name": "FOO_IOCTL6",
+ "type": {
+ "int": {
+ "byte_size": 1,
+ "is_const": true
+ }
+ }
+ },
+ {
+ "name": "FOO_IOCTL7",
+ "type": {
+ "int": {
+ "byte_size": 1,
+ "is_const": true
+ }
+ }
+ },
+ {
+ "name": "UNUSED_IOCTL1",
+ "type": {
+ "int": {
+ "byte_size": 1,
+ "is_const": true
+ }
+ }
+ },
+ {
+ "name": "UNUSED_IOCTL2",
+ "type": {
+ "int": {
+ "byte_size": 1,
+ "is_const": true
+ }
+ }
}
]
} \ No newline at end of file
diff --git a/tools/syz-declextract/testdata/file_operations.c.txt b/tools/syz-declextract/testdata/file_operations.c.txt
index e94856980..f37a386db 100644
--- a/tools/syz-declextract/testdata/file_operations.c.txt
+++ b/tools/syz-declextract/testdata/file_operations.c.txt
@@ -6,6 +6,7 @@ type auto_todo int8
include <vdso/bits.h>
include <linux/types.h>
+include <linux/usbdevice_fs.h>
include <include/uapi/file_operations.h>
resource fd_foo_file_operations[fd]
@@ -18,6 +19,8 @@ ioctl$auto_FOO_IOCTL2(fd fd_foo_file_operations, cmd const[FOO_IOCTL2], arg ptr[
ioctl$auto_FOO_IOCTL3(fd fd_foo_file_operations, cmd const[FOO_IOCTL3], arg ptr[in, foo_ioctl_arg$auto])
ioctl$auto_FOO_IOCTL4(fd fd_foo_file_operations, cmd const[FOO_IOCTL4], arg ptr[inout, foo_ioctl_arg$auto])
ioctl$auto_FOO_IOCTL5(fd fd_foo_file_operations, cmd const[FOO_IOCTL5], arg ptr[inout, foo_ioctl_arg$auto])
+ioctl$auto_FOO_IOCTL6(fd fd_foo_file_operations, cmd const[FOO_IOCTL6], arg const[0])
+ioctl$auto_FOO_IOCTL7(fd fd_foo_file_operations, cmd const[FOO_IOCTL7], arg const[0])
foo_ioctl_arg$auto {
a int32
diff --git a/tools/syz-declextract/testdata/functions.c.json b/tools/syz-declextract/testdata/functions.c.json
index eb1b3b880..ecb95affc 100644
--- a/tools/syz-declextract/testdata/functions.c.json
+++ b/tools/syz-declextract/testdata/functions.c.json
@@ -3,256 +3,317 @@
{
"name": "__do_sys_functions",
"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
- }
- }
- },
+ "scopes": [
{
- "src": {
- "return": {
- "func": "func_baz"
+ "arg": -1,
+ "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"
+ }
+ }
}
- },
- "dst": {
- "return": {
- "func": "__do_sys_functions"
- }
- }
+ ]
}
]
},
{
"name": "__fget_light",
- "file": "functions.c"
+ "file": "functions.c",
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "alloc_fd",
"file": "functions.c",
- "loc": 1
+ "scopes": [
+ {
+ "arg": -1,
+ "loc": 1
+ }
+ ]
},
{
"name": "atomic_load32",
"file": "include/types.h",
"is_static": true,
- "loc": 1
+ "scopes": [
+ {
+ "arg": -1,
+ "loc": 1
+ }
+ ]
},
{
"name": "atomic_load64",
"file": "include/types.h",
- "loc": 1
+ "scopes": [
+ {
+ "arg": -1,
+ "loc": 1
+ }
+ ]
},
{
"name": "from_kuid",
"file": "functions.c",
- "loc": 1
+ "scopes": [
+ {
+ "arg": -1,
+ "loc": 1
+ }
+ ]
},
{
"name": "func_bar",
"file": "functions.c",
"is_static": true,
- "loc": 1,
- "calls": [
- "func_foo"
+ "scopes": [
+ {
+ "arg": -1,
+ "loc": 1,
+ "calls": [
+ "func_foo"
+ ]
+ }
]
},
{
"name": "func_baz",
"file": "functions.c",
- "loc": 8,
- "calls": [
- "func_foo",
- "func_bar",
- "from_kuid",
- "alloc_fd"
- ],
- "facts": [
+ "scopes": [
{
- "src": {
- "return": {
- "func": "from_kuid"
+ "arg": -1,
+ "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"
+ }
+ }
}
- },
- "dst": {
- "return": {
- "func": "func_baz"
- }
- }
- },
- {
- "src": {
- "return": {
- "func": "alloc_fd"
- }
- },
- "dst": {
- "return": {
- "func": "func_baz"
- }
- }
+ ]
}
]
},
{
"name": "func_foo",
"file": "functions.c",
- "is_static": true
+ "is_static": true,
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "func_qux",
"file": "functions.c",
- "loc": 2,
- "calls": [
- "alloc_fd"
- ],
- "facts": [
+ "scopes": [
{
- "src": {
- "return": {
- "func": "alloc_fd"
+ "arg": -1,
+ "loc": 2,
+ "calls": [
+ "alloc_fd"
+ ],
+ "facts": [
+ {
+ "src": {
+ "return": {
+ "func": "alloc_fd"
+ }
+ },
+ "dst": {
+ "local": {
+ "name": "fd"
+ }
+ }
+ },
+ {
+ "src": {
+ "local": {
+ "name": "fd"
+ }
+ },
+ "dst": {
+ "return": {
+ "func": "func_qux"
+ }
+ }
}
- },
- "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"
- }
- }
- },
+ "scopes": [
{
- "src": {
- "return": {
- "func": "typing1"
+ "arg": -1,
+ "loc": 5,
+ "calls": [
+ "typing1"
+ ],
+ "facts": [
+ {
+ "src": {
+ "field": {
+ "struct": "Typed",
+ "field": "b"
+ }
+ },
+ "dst": {
+ "field": {
+ "struct": "Typed",
+ "field": "a"
+ }
+ }
+ },
+ {
+ "src": {
+ "return": {
+ "func": "typing1"
+ }
+ },
+ "dst": {
+ "local": {
+ "name": "l"
+ }
+ }
+ },
+ {
+ "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": {
+ "local": {
+ "name": "l"
+ }
+ },
+ "dst": {
+ "field": {
+ "struct": "Typed",
+ "field": "c"
+ }
+ }
+ },
+ {
+ "src": {
+ "local": {
+ "name": "l"
+ }
+ },
+ "dst": {
+ "return": {
+ "func": "typing"
+ }
+ }
}
- },
- "dst": {
- "local": {
- "name": "l"
- }
- }
- },
- {
- "src": {
- "local": {
- "name": "l"
- }
- },
- "dst": {
- "return": {
- "func": "typing"
- }
- }
+ ]
}
]
},
{
"name": "typing1",
"file": "functions.c",
- "loc": 1,
- "facts": [
+ "scopes": [
{
- "src": {
- "argument": {
- "func": "typing1",
- "arg": 0
+ "arg": -1,
+ "loc": 1,
+ "facts": [
+ {
+ "src": {
+ "argument": {
+ "func": "typing1",
+ "arg": 0
+ }
+ },
+ "dst": {
+ "return": {
+ "func": "typing1"
+ }
+ }
}
- },
- "dst": {
- "return": {
- "func": "typing1"
- }
- }
+ ]
}
]
}
diff --git a/tools/syz-declextract/testdata/include/fs.h b/tools/syz-declextract/testdata/include/fs.h
index a5c838595..33782d1ee 100644
--- a/tools/syz-declextract/testdata/include/fs.h
+++ b/tools/syz-declextract/testdata/include/fs.h
@@ -7,6 +7,11 @@ struct file_operations {
void (*write)(void);
void (*read_iter)(void);
void (*write_iter)(void);
- void (*unlocked_ioctl)(unsigned int);
+ void (*unlocked_ioctl)(void*, unsigned int, unsigned long);
void (*mmap)(void);
};
+
+int alloc_fd();
+void __fget_light(int fd);
+int from_kuid();
+
diff --git a/tools/syz-declextract/testdata/include/uapi/file_operations.h b/tools/syz-declextract/testdata/include/uapi/file_operations.h
index 6a2a8d259..f81d6886d 100644
--- a/tools/syz-declextract/testdata/include/uapi/file_operations.h
+++ b/tools/syz-declextract/testdata/include/uapi/file_operations.h
@@ -8,6 +8,10 @@
#define FOO_IOCTL3 _IOR('c', 3, struct foo_ioctl_arg)
#define FOO_IOCTL4 _IOW('c', 4, struct foo_ioctl_arg)
#define FOO_IOCTL5 _IOWR('c', 5, struct foo_ioctl_arg)
+#define FOO_IOCTL6 _IO('c', 6)
+#define FOO_IOCTL7 _IO('c', 7)
+#define FOO_IOCTL8 _IO('c', 8)
+#define FOO_IOCTL9 _IO('c', 9)
struct foo_ioctl_arg {
int a, b;
diff --git a/tools/syz-declextract/testdata/io_uring.c.json b/tools/syz-declextract/testdata/io_uring.c.json
index da91ce1b6..60ac44818 100644
--- a/tools/syz-declextract/testdata/io_uring.c.json
+++ b/tools/syz-declextract/testdata/io_uring.c.json
@@ -2,31 +2,66 @@
"functions": [
{
"name": "io_eopnotsupp_prep",
- "file": "io_uring.c"
+ "file": "io_uring.c",
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "io_nop",
- "file": "io_uring.c"
+ "file": "io_uring.c",
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "io_nop_prep",
- "file": "io_uring.c"
+ "file": "io_uring.c",
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "io_read",
- "file": "io_uring.c"
+ "file": "io_uring.c",
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "io_readv_prep",
- "file": "io_uring.c"
+ "file": "io_uring.c",
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "io_write",
- "file": "io_uring.c"
+ "file": "io_uring.c",
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "io_writev_prep",
- "file": "io_uring.c"
+ "file": "io_uring.c",
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
}
],
"consts": [
diff --git a/tools/syz-declextract/testdata/netlink.c.json b/tools/syz-declextract/testdata/netlink.c.json
index 35b2a79b5..9571730f7 100644
--- a/tools/syz-declextract/testdata/netlink.c.json
+++ b/tools/syz-declextract/testdata/netlink.c.json
@@ -4,37 +4,72 @@
"name": "atomic_load32",
"file": "include/types.h",
"is_static": true,
- "loc": 1
+ "scopes": [
+ {
+ "arg": -1,
+ "loc": 1
+ }
+ ]
},
{
"name": "atomic_load64",
"file": "include/types.h",
- "loc": 1
+ "scopes": [
+ {
+ "arg": -1,
+ "loc": 1
+ }
+ ]
},
{
"name": "bar_cmd",
"file": "netlink.c",
- "is_static": true
+ "is_static": true,
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "bar_doit",
"file": "netlink.c",
- "is_static": true
+ "is_static": true,
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "bar_post_doit",
"file": "netlink.c",
- "is_static": true
+ "is_static": true,
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "bar_pre_doit",
"file": "netlink.c",
- "is_static": true
+ "is_static": true,
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
},
{
"name": "foo_cmd",
"file": "netlink.c",
- "is_static": true
+ "is_static": true,
+ "scopes": [
+ {
+ "arg": -1
+ }
+ ]
}
],
"consts": [
diff --git a/tools/syz-declextract/testdata/scopes.c b/tools/syz-declextract/testdata/scopes.c
new file mode 100644
index 000000000..56c1638d1
--- /dev/null
+++ b/tools/syz-declextract/testdata/scopes.c
@@ -0,0 +1,30 @@
+// Copyright 2024 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.
+
+#include "include/fs.h"
+#include "include/syscall.h"
+#include "include/uapi/file_operations.h"
+
+SYSCALL_DEFINE1(scopes0, int x, long cmd, long aux) {
+ int tmp = 0;
+ __fget_light(aux);
+ switch (cmd) {
+ case FOO_IOCTL1:
+ __fget_light(x);
+ break;
+ case FOO_IOCTL2:
+ case FOO_IOCTL3:
+ tmp = alloc_fd();
+ return tmp;
+ case FOO_IOCTL4 ... FOO_IOCTL4 + 2:
+ tmp++;
+ break;
+ case 100 ... 102:
+ tmp++;
+ break;
+ default:
+ tmp = cmd;
+ break;
+ }
+ return tmp;
+}
diff --git a/tools/syz-declextract/testdata/scopes.c.info b/tools/syz-declextract/testdata/scopes.c.info
new file mode 100644
index 000000000..f3a8f9cf3
--- /dev/null
+++ b/tools/syz-declextract/testdata/scopes.c.info
@@ -0,0 +1 @@
+SYSCALL scopes0 func:__do_sys_scopes0 loc:20 access:unknown manual_desc:false auto_desc:true file:scopes.c subsystem:kernel
diff --git a/tools/syz-declextract/testdata/scopes.c.json b/tools/syz-declextract/testdata/scopes.c.json
new file mode 100644
index 000000000..2a497dbe1
--- /dev/null
+++ b/tools/syz-declextract/testdata/scopes.c.json
@@ -0,0 +1,286 @@
+{
+ "functions": [
+ {
+ "name": "__do_sys_scopes0",
+ "file": "scopes.c",
+ "scopes": [
+ {
+ "arg": -1,
+ "loc": 4,
+ "calls": [
+ "__fget_light"
+ ],
+ "facts": [
+ {
+ "src": {
+ "argument": {
+ "func": "__do_sys_scopes0",
+ "arg": 2
+ }
+ },
+ "dst": {
+ "argument": {
+ "func": "__fget_light",
+ "arg": 0
+ }
+ }
+ },
+ {
+ "src": {
+ "local": {
+ "name": "tmp"
+ }
+ },
+ "dst": {
+ "return": {
+ "func": "__do_sys_scopes0"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "arg": 1,
+ "values": [
+ "FOO_IOCTL1"
+ ],
+ "loc": 3,
+ "calls": [
+ "__fget_light"
+ ],
+ "facts": [
+ {
+ "src": {
+ "argument": {
+ "func": "__do_sys_scopes0",
+ "arg": 0
+ }
+ },
+ "dst": {
+ "argument": {
+ "func": "__fget_light",
+ "arg": 0
+ }
+ }
+ }
+ ]
+ },
+ {
+ "arg": 1,
+ "values": [
+ "FOO_IOCTL2",
+ "FOO_IOCTL3"
+ ],
+ "loc": 4,
+ "calls": [
+ "alloc_fd"
+ ],
+ "facts": [
+ {
+ "src": {
+ "return": {
+ "func": "alloc_fd"
+ }
+ },
+ "dst": {
+ "local": {
+ "name": "tmp"
+ }
+ }
+ },
+ {
+ "src": {
+ "local": {
+ "name": "tmp"
+ }
+ },
+ "dst": {
+ "return": {
+ "func": "__do_sys_scopes0"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "arg": 1,
+ "values": [
+ "FOO_IOCTL4",
+ "1074291461",
+ "1074291462"
+ ],
+ "loc": 3
+ },
+ {
+ "arg": 1,
+ "values": [
+ "100",
+ "101",
+ "102"
+ ],
+ "loc": 3
+ },
+ {
+ "arg": 1,
+ "loc": 3,
+ "facts": [
+ {
+ "src": {
+ "argument": {
+ "func": "__do_sys_scopes0",
+ "arg": 1
+ }
+ },
+ "dst": {
+ "local": {
+ "name": "tmp"
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "consts": [
+ {
+ "name": "FOO_IOCTL1",
+ "filename": "include/uapi/file_operations.h",
+ "value": 25345
+ },
+ {
+ "name": "FOO_IOCTL2",
+ "filename": "include/uapi/file_operations.h",
+ "value": 2147771138
+ },
+ {
+ "name": "FOO_IOCTL3",
+ "filename": "include/uapi/file_operations.h",
+ "value": 2148033283
+ },
+ {
+ "name": "FOO_IOCTL4",
+ "filename": "include/uapi/file_operations.h",
+ "value": 1074291460
+ }
+ ],
+ "structs": [
+ {
+ "name": "foo_ioctl_arg",
+ "byte_size": 8,
+ "align": 4,
+ "fields": [
+ {
+ "name": "a",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ },
+ {
+ "name": "b",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "syscalls": [
+ {
+ "func": "__do_sys_scopes0",
+ "args": [
+ {
+ "name": "x",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ }
+ },
+ {
+ "name": "cmd",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 8,
+ "name": "long",
+ "base": "long"
+ }
+ }
+ },
+ {
+ "name": "aux",
+ "counted_by": -1,
+ "type": {
+ "int": {
+ "byte_size": 8,
+ "name": "long",
+ "base": "long"
+ }
+ }
+ }
+ ],
+ "source_file": "scopes.c"
+ }
+ ],
+ "ioctls": [
+ {
+ "name": "FOO_IOCTL1",
+ "type": {
+ "int": {
+ "byte_size": 1,
+ "is_const": true
+ }
+ }
+ },
+ {
+ "name": "FOO_IOCTL2",
+ "type": {
+ "ptr": {
+ "elem": {
+ "int": {
+ "byte_size": 4,
+ "name": "int",
+ "base": "int"
+ }
+ },
+ "is_const": true
+ }
+ }
+ },
+ {
+ "name": "FOO_IOCTL3",
+ "type": {
+ "ptr": {
+ "elem": {
+ "struct": "foo_ioctl_arg"
+ },
+ "is_const": true
+ }
+ }
+ },
+ {
+ "name": "FOO_IOCTL4",
+ "type": {
+ "ptr": {
+ "elem": {
+ "struct": "foo_ioctl_arg"
+ }
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/tools/syz-declextract/testdata/scopes.c.txt b/tools/syz-declextract/testdata/scopes.c.txt
new file mode 100644
index 000000000..aec7f7f2d
--- /dev/null
+++ b/tools/syz-declextract/testdata/scopes.c.txt
@@ -0,0 +1,7 @@
+# Code generated by syz-declextract. DO NOT EDIT.
+
+meta automatic
+
+type auto_todo int8
+
+scopes0$auto(x int32, cmd intptr, aux intptr)
diff --git a/tools/syz-declextract/testdata/syscall.c.json b/tools/syz-declextract/testdata/syscall.c.json
index 735ac7dc9..4e466a3ae 100644
--- a/tools/syz-declextract/testdata/syscall.c.json
+++ b/tools/syz-declextract/testdata/syscall.c.json
@@ -3,12 +3,22 @@
{
"name": "__do_sys_chmod",
"file": "syscall.c",
- "loc": 1
+ "scopes": [
+ {
+ "arg": -1,
+ "loc": 1
+ }
+ ]
},
{
"name": "__do_sys_open",
"file": "syscall.c",
- "loc": 1
+ "scopes": [
+ {
+ "arg": -1,
+ "loc": 1
+ }
+ ]
}
],
"syscalls": [
diff --git a/tools/syz-declextract/testdata/types.c.info b/tools/syz-declextract/testdata/types.c.info
index ac9903fbe..7c4e66ca1 100644
--- a/tools/syz-declextract/testdata/types.c.info
+++ b/tools/syz-declextract/testdata/types.c.info
@@ -1,2 +1,2 @@
SYSCALL align_syscall func:__do_sys_align_syscall loc:1 access:unknown manual_desc:false auto_desc:true file:types.c subsystem:kernel
-SYSCALL types_syscall func:__do_sys_types_syscall loc:2 access:unknown manual_desc:false auto_desc:true file:types.c subsystem:kernel
+SYSCALL types_syscall func:__do_sys_types_syscall loc:1 access:unknown manual_desc:false auto_desc:true file:types.c subsystem:kernel
diff --git a/tools/syz-declextract/testdata/types.c.json b/tools/syz-declextract/testdata/types.c.json
index 6c479dffb..90d3be2a9 100644
--- a/tools/syz-declextract/testdata/types.c.json
+++ b/tools/syz-declextract/testdata/types.c.json
@@ -3,115 +3,130 @@
{
"name": "__do_sys_align_syscall",
"file": "types.c",
- "loc": 1
+ "scopes": [
+ {
+ "arg": -1,
+ "loc": 1
+ }
+ ]
},
{
"name": "__do_sys_types_syscall",
"file": "types.c",
- "loc": 2
+ "scopes": [
+ {
+ "arg": -1,
+ "loc": 1
+ }
+ ]
},
{
"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"
+ "scopes": [
+ {
+ "arg": -1,
+ "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"
+ }
+ }
}
- }
+ ]
}
]
}