From dcb1eebb7ffdd179de39e7cb89ca085368ee69d5 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Mon, 18 Jun 2018 19:45:48 +0200 Subject: prog: more precise constructor calculation Currently a call that both accepts and creates a resource self-justifies itself and thus is always enabled. A good example is accept call. Accepts are always self-enable and thus enable all other syscalls that work with the socket. Calculate TransitivelyEnabledCalls in the opposite direction to resolve this. Start with empty set of enable syscalls, then enable syscalls that don't accept any resources, then enable syscalls that accept resources created by the previous batch of syscalls, and so on. This prevents self-enablement of accept. --- prog/decl_test.go | 35 ++++++++++----- prog/resources.go | 128 +++++++++++++++++++++++++++++++++--------------------- 2 files changed, 102 insertions(+), 61 deletions(-) (limited to 'prog') diff --git a/prog/decl_test.go b/prog/decl_test.go index 0f727aa7f..ac353e66a 100644 --- a/prog/decl_test.go +++ b/prog/decl_test.go @@ -14,11 +14,11 @@ func TestResourceCtors(t *testing.T) { } testEachTarget(t, func(t *testing.T, target *Target) { for _, c := range target.Syscalls { - for _, res := range c.inputResources() { - if len(target.calcResourceCtors(res.Desc.Kind, true)) == 0 { + for _, res := range target.inputResources(c) { + if len(target.calcResourceCtors(res.Kind, true)) == 0 { t.Errorf("call %v requires input resource %v,"+ " but there are no calls that can create this resource", - c.Name, res.Desc.Name) + c.Name, res.Name) } } } @@ -31,18 +31,29 @@ func TestTransitivelyEnabledCalls(t *testing.T) { for _, c := range target.Syscalls { calls[c] = true } - if trans, disabled := target.TransitivelyEnabledCalls(calls); len(disabled) != 0 { + enabled, disabled := target.TransitivelyEnabledCalls(calls) + for c, ok := range enabled { + if !ok { + t.Fatalf("syscalls %v is false in enabled map", c.Name) + } + } + if target.OS == "test" { + for c := range enabled { + if c.CallName == "unsupported" { + t.Errorf("call %v is not disabled", c.Name) + } + } for c, reason := range disabled { - t.Logf("disabled %v: %v", c.Name, reason) + if c.CallName != "unsupported" { + t.Errorf("call %v is disabled: %v", c.Name, reason) + } } - t.Fatalf("can't create some resource") - } else if len(trans) != len(calls) { - t.Fatalf("transitive syscalls are not full") } else { - for c, ok := range trans { - if !ok { - t.Fatalf("syscalls %v is false in transitive map", c.Name) - } + if len(enabled) != len(target.Syscalls) { + t.Errorf("some calls are disabled: %v/%v", len(enabled), len(target.Syscalls)) + } + for c, reason := range disabled { + t.Errorf("disabled %v: %v", c.Name, reason) } } }) diff --git a/prog/resources.go b/prog/resources.go index 68fbe1599..bddb158d9 100644 --- a/prog/resources.go +++ b/prog/resources.go @@ -7,6 +7,13 @@ import ( "fmt" ) +// We need to support structs as resources, +// but for now we just special-case timespec/timeval. +var timespecRes = &ResourceDesc{ + Name: "timespec", + Kind: []string{"timespec"}, +} + func (target *Target) calcResourceCtors(kind []string, precise bool) []*Syscall { // Find calls that produce the necessary resources. var metas []*Syscall @@ -28,6 +35,11 @@ func (target *Target) calcResourceCtors(kind []string, precise bool) []*Syscall metas = append(metas, meta) } } + if kind[0] == timespecRes.Name { + if c := target.SyscallMap["clock_gettime"]; c != nil { + metas = append(metas, c) + } + } return metas } @@ -72,82 +84,100 @@ func isCompatibleResourceImpl(dst, src []string, precise bool) bool { return true } -func (c *Syscall) inputResources() []*ResourceType { - var resources []*ResourceType +func (target *Target) inputResources(c *Syscall) []*ResourceDesc { + var resources []*ResourceDesc + ForeachType(c, func(typ Type) { + if typ.Dir() == DirOut { + return + } + switch typ1 := typ.(type) { + case *ResourceType: + if !typ1.IsOptional { + resources = append(resources, typ1.Desc) + } + case *StructType: + if target.OS == "linux" && (typ1.Name() == "timespec" || typ1.Name() == "timeval") { + resources = append(resources, timespecRes) + } + } + }) + return resources +} + +func (target *Target) outputResources(c *Syscall) []*ResourceDesc { + var resources []*ResourceDesc ForeachType(c, func(typ Type) { switch typ1 := typ.(type) { case *ResourceType: - if typ1.Dir() != DirOut && !typ1.IsOptional { - resources = append(resources, typ1) + if typ1.Dir() != DirIn { + resources = append(resources, typ1.Desc) } } }) + if c.CallName == "clock_gettime" { + resources = append(resources, timespecRes) + } return resources } func (target *Target) TransitivelyEnabledCalls(enabled map[*Syscall]bool) (map[*Syscall]bool, map[*Syscall]string) { supported := make(map[*Syscall]bool) disabled := make(map[*Syscall]string) + canCreate := make(map[string]bool) + inputResources := make(map[*Syscall][]*ResourceDesc) for c := range enabled { - supported[c] = true - } - inputResources := make(map[*Syscall][]*ResourceType) - ctors := make(map[string][]*Syscall) - for c := range supported { - inputs := c.inputResources() - inputResources[c] = inputs - for _, res := range inputs { - if _, ok := ctors[res.Desc.Name]; ok { - continue - } - ctors[res.Desc.Name] = target.calcResourceCtors(res.Desc.Kind, true) - } + inputResources[c] = target.inputResources(c) } for { n := len(supported) - haveGettime := supported[target.SyscallMap["clock_gettime"]] - for c := range supported { - cantCreate := "" - var resourceCtors []*Syscall + for c := range enabled { + if supported[c] { + continue + } + ready := true for _, res := range inputResources[c] { - noctors := true - for _, ctor := range ctors[res.Desc.Name] { - if supported[ctor] { - noctors = false - break - } - } - if noctors { - cantCreate = res.Desc.Name - resourceCtors = ctors[res.Desc.Name] + if !canCreate[res.Name] { + ready = false break } } - // We need to support structs as resources, - // but for now we just special-case timespec/timeval. - if cantCreate == "" && !haveGettime && target.SyscallMap["clock_gettime"] != nil { - ForeachType(c, func(typ Type) { - if a, ok := typ.(*StructType); ok && a.Dir() != DirOut && - (a.Name() == "timespec" || a.Name() == "timeval") { - cantCreate = a.Name() - resourceCtors = []*Syscall{target.SyscallMap["clock_gettime"]} + if ready { + supported[c] = true + for _, res := range target.outputResources(c) { + for _, kind := range res.Kind { + canCreate[kind] = true } - }) - } - if cantCreate != "" { - delete(supported, c) - var ctorNames []string - for _, ctor := range resourceCtors { - ctorNames = append(ctorNames, ctor.Name) } - disabled[c] = fmt.Sprintf("no syscalls can create resource %v,"+ - " enable some syscalls that can create it %v", - cantCreate, ctorNames) } } if n == len(supported) { break } } + ctors := make(map[string][]string) + for c := range enabled { + if supported[c] { + continue + } + for _, res := range inputResources[c] { + if canCreate[res.Name] { + continue + } + if ctors[res.Name] == nil { + var names []string + for _, call := range target.calcResourceCtors(res.Kind, true) { + names = append(names, call.Name) + } + ctors[res.Name] = names + } + disabled[c] = fmt.Sprintf("no syscalls can create resource %v,"+ + " enable some syscalls that can create it %v", + res.Name, ctors[res.Name]) + break + } + } + if len(enabled) != len(supported)+len(disabled) { + panic("lost syscalls") + } return supported, disabled } -- cgit mrf-deployment