From f5ee62f7ee98f53dced33b767e59f47b35017568 Mon Sep 17 00:00:00 2001 From: Paul Chaignon Date: Fri, 6 Oct 2023 20:02:41 +0200 Subject: prog: fix missing required input resources While reviewing the changes introduced by previous commits, Aleksandr noticed a bug in the logic to compute all required input resources of a given syscall (function getInputResources). This commit fixes the bug. That function has an optimization to avoid walking structs and unions twice. That optimization however fails to consider the direction and optional bit. For example, if a struct is first encountered with DirOut (== output resource), it will never be analyzed again and will not be included in getInputResources' returned values. That same struct could however be included elsewhere with DirIn. The exact same issue affects the optional bit since optional resources are now also skipped by getInputResources. One approach to fix this is to only consider a struct or union as 'seen' if it was seen with the same direction and optional bit. This commit implements that approach. Fixes: 852e3d2eae98 ("sys: support recursive structs") Reported-by: Aleksandr Nogikh Signed-off-by: Paul Chaignon --- prog/types.go | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) (limited to 'prog') diff --git a/prog/types.go b/prog/types.go index 45136f079..cbba259c9 100644 --- a/prog/types.go +++ b/prog/types.go @@ -704,10 +704,20 @@ func ForeachCallType(meta *Syscall, f func(t Type, ctx *TypeCtx)) { } func foreachTypeImpl(meta *Syscall, preorder bool, f func(t Type, ctx *TypeCtx)) { + // We need seen to be keyed by the type, the direction, and the optionality + // bit. Even if the first time we see a type it is optional or DirOut, it + // could be required or DirIn on another path. So to ensure that the + // information we report to the caller is correct, we need to visit both + // occurrences. + type seenKey struct { + t Type + d Dir + o bool + } // Note: we specifically don't create seen in ForeachType. // It would prune recursion more (across syscalls), but lots of users need to // visit each struct per-syscall (e.g. prio, used resources). - seen := make(map[Type]bool) + seen := make(map[seenKey]bool) var rec func(*Type, Dir, bool) rec = func(ptr *Type, dir Dir, optional bool) { if _, ref := (*ptr).(Ref); !ref { @@ -726,18 +736,28 @@ func foreachTypeImpl(meta *Syscall, preorder bool, f func(t Type, ctx *TypeCtx)) case *ArrayType: rec(&a.Elem, dir, optional) case *StructType: - if seen[a] { + key := seenKey{ + t: a, + d: dir, + o: optional, + } + if seen[key] { break // prune recursion via pointers to structs/unions } - seen[a] = true + seen[key] = true for i, f := range a.Fields { rec(&a.Fields[i].Type, f.Dir(dir), optional) } case *UnionType: - if seen[a] { + key := seenKey{ + t: a, + d: dir, + o: optional, + } + if seen[key] { break // prune recursion via pointers to structs/unions } - seen[a] = true + seen[key] = true for i, f := range a.Fields { rec(&a.Fields[i].Type, f.Dir(dir), optional) } -- cgit mrf-deployment