diff --git a/nogo.yaml b/nogo.yaml index c9ca8cc53..d93768bd1 100644 --- a/nogo.yaml +++ b/nogo.yaml @@ -88,14 +88,7 @@ global: - pkg/shim/runtimeoptions/v14/runtimeoptions_cri.go analyzers: asmdecl: - generated: - suppress: - # Non-conventional functions. - - "\\[amd64\\].*sysret: RET without writing to 8-byte" - - "\\[amd64\\].*iret: RET without writing to 8-byte" - - "\\[amd64\\].*exception: use of .* points beyond argument frame" - - "\\[arm64\\].*HaltEl1ExceptionAndResume: unknown variable" - - "\\[arm64\\].*HaltEl1ExceptionAndResume: use of .* points beyond argument frame" + generated: # Enabled. external: exclude: [".*"] # Disabled. internal: @@ -109,6 +102,12 @@ analyzers: - "wrmsr: invalid offset value" - "rdmsr: invalid MOVL of ret" - "rdmsr: invalid offset ret" + # Non-conventional functions. + - "\\[amd64\\].*sysret: RET without writing to 8-byte" + - "\\[amd64\\].*iret: RET without writing to 8-byte" + - "\\[amd64\\].*exception: use of .* points beyond argument frame" + - "\\[arm64\\].*HaltEl1ExceptionAndResume: unknown variable" + - "\\[arm64\\].*HaltEl1ExceptionAndResume: use of .* points beyond argument frame" assign: external: exclude: diff --git a/pkg/abi/linux/BUILD b/pkg/abi/linux/BUILD index aec99b169..7f0b132e5 100644 --- a/pkg/abi/linux/BUILD +++ b/pkg/abi/linux/BUILD @@ -1,6 +1,4 @@ -load("//tools:arch.bzl", "arch_genrule") load("//tools:defs.bzl", "go_library", "go_test") -load("//tools/nogo:defs.bzl", "nogo_facts") # Package linux contains the constants and types needed to interface with a # Linux kernel. It should be used instead of syscall or golang.org/x/sys/unix @@ -8,19 +6,6 @@ load("//tools/nogo:defs.bzl", "nogo_facts") package(licenses = ["notice"]) -nogo_facts( - name = "iouring_offsets", - srcs = ["iouring.go"], - output = "iouring_offsets_impl.go", - template = "iouring_offsets.tmpl", -) - -arch_genrule( - name = "iouring_offsets_arch", - src = ":iouring_offsets", - template = "iouring_offsets_%s.go", -) - go_library( name = "linux", srcs = [ @@ -91,7 +76,6 @@ go_library( "utsname.go", "wait.go", "xattr.go", - ":iouring_offsets_arch", ], marshal = True, visibility = ["//visibility:public"], diff --git a/pkg/abi/linux/iouring.go b/pkg/abi/linux/iouring.go index f208d6b7b..0eeb62abd 100644 --- a/pkg/abi/linux/iouring.go +++ b/pkg/abi/linux/iouring.go @@ -192,3 +192,47 @@ type IOUringSqe struct { addr3 uint64 _ uint64 } + +const ( + _IOSqRingOffset = 0 // +checkoffset . IORings.Sq + _IOSqRingOffsetHead = 0 // +checkoffset . IOUring.Head + _IOSqRingOffsetTail = 64 // +checkoffset . IOUring.Tail + _IOSqRingOffsetMask = 256 // +checkoffset . IORings.SqRingMask + _IOSqRingOffsetEntries = 264 // +checkoffset . IORings.SqRingEntries + _IOSqRingOffsetFlags = 276 // +checkoffset . IORings.sqFlags + _IOSqRingOffsetDropped = 272 // +checkoffset . IORings.sqDropped +) + +// PreComputedIOSqRingOffsets returns precomputed values for IOSqRingOffsets. +func PreComputedIOSqRingOffsets() IOSqRingOffsets { + return IOSqRingOffsets{ + Head: _IOSqRingOffset + _IOSqRingOffsetHead, + Tail: _IOSqRingOffset + _IOSqRingOffsetTail, + RingMask: _IOSqRingOffsetMask, + RingEntries: _IOSqRingOffsetEntries, + Flags: _IOSqRingOffsetFlags, + Dropped: _IOSqRingOffsetDropped, + } +} + +const ( + _IOCqRingOffset = 128 // +checkoffset . IORings.Cq + _IOCqRingOffsetHead = 0 // +checkoffset . IOUring.Head + _IOCqRingOffsetTail = 64 // +checkoffset . IOUring.Tail + _IOCqRingOffsetMask = 260 // +checkoffset . IORings.CqRingMask + _IOCqRingOffsetEntries = 268 // +checkoffset . IORings.CqRingEntries + _IOCqRingOffsetFlags = 280 // +checkoffset . IORings.cqFlags + _IOCqRingOffsetOverflow = 284 // +checkoffset . IORings.CqOverflow +) + +// PreComputedIOCqRingOffsets returns precomputed values for IOCqRingOffsets. +func PreComputedIOCqRingOffsets() IOCqRingOffsets { + return IOCqRingOffsets{ + Head: _IOCqRingOffset + _IOCqRingOffsetHead, + Tail: _IOCqRingOffset + _IOCqRingOffsetTail, + RingMask: _IOCqRingOffsetMask, + RingEntries: _IOCqRingOffsetEntries, + Overflow: _IOCqRingOffsetOverflow, + Flags: _IOCqRingOffsetFlags, + } +} diff --git a/pkg/abi/linux/iouring_offsets.tmpl b/pkg/abi/linux/iouring_offsets.tmpl deleted file mode 100644 index 7632dd617..000000000 --- a/pkg/abi/linux/iouring_offsets.tmpl +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2022 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package linux - -// PreComputedIOSqRingOffsets returns precomputed values for IOSqRingOffsets. -func PreComputedIOSqRingOffsets() IOSqRingOffsets { - return IOSqRingOffsets{ - Head: {{ .IORings.Sq.Offset }} + {{ .IOUring.Head.Offset }}, - Tail: {{ .IORings.Sq.Offset }} + {{ .IOUring.Tail.Offset }}, - RingMask: {{ .IORings.SqRingMask.Offset }}, - RingEntries: {{ .IORings.SqRingEntries.Offset }}, - Flags: {{ .IORings.sqFlags.Offset }}, - Dropped: {{ .IORings.sqDropped.Offset }}, - } -} - -// PreComputedIOCqRingOffsets returns precomputed values for IOCqRingOffsets. -func PreComputedIOCqRingOffsets() IOCqRingOffsets { - return IOCqRingOffsets { - Head: {{ .IORings.Cq.Offset }} + {{ .IOUring.Head.Offset }}, - Tail: {{ .IORings.Cq.Offset }} + {{ .IOUring.Tail.Offset }}, - RingMask: {{ .IORings.CqRingMask.Offset }}, - RingEntries: {{ .IORings.CqRingEntries.Offset }}, - Overflow: {{ .IORings.CqOverflow.Offset }}, - Flags: {{ .IORings.cqFlags.Offset }}, - } -} diff --git a/pkg/goid/BUILD b/pkg/goid/BUILD index 49e0bdc4c..08832a8ae 100644 --- a/pkg/goid/BUILD +++ b/pkg/goid/BUILD @@ -1,30 +1,13 @@ -load("//tools:arch.bzl", "arch_genrule", "select_arch") load("//tools:defs.bzl", "go_library", "go_test") -load("//tools/nogo:defs.bzl", "nogo_facts") package(licenses = ["notice"]) -nogo_facts( - name = "goid_impl", - srcs = ["goid.go"], - output = "goid_impl.s", - template = select_arch( - amd64 = "goid_amd64.s", - arm64 = "goid_arm64.s", - ), -) - -arch_genrule( - name = "goid_impl_arch", - src = ":goid_impl", - template = "goid_impl_%s.s", -) - go_library( name = "goid", srcs = [ "goid.go", - ":goid_impl_arch", + "goid_amd64.s", + "goid_arm64.s", ], stateify = False, visibility = ["//visibility:public"], diff --git a/pkg/goid/goid.go b/pkg/goid/goid.go index 8d6a44e9b..153176159 100644 --- a/pkg/goid/goid.go +++ b/pkg/goid/goid.go @@ -16,12 +16,9 @@ package goid import ( - "runtime" + _ "runtime" // For facts in assembly files. ) -// Dummy references for facts. -const _ = runtime.Compiler - // goid returns the current goid, it is defined in assembly. func goid() int64 diff --git a/pkg/goid/goid_amd64.s b/pkg/goid/goid_amd64.s index a95858cb1..8cdf46f17 100644 --- a/pkg/goid/goid_amd64.s +++ b/pkg/goid/goid_amd64.s @@ -14,7 +14,7 @@ #include "textflag.h" -#define GOID_OFFSET {{ .import.runtime.g.goid.Offset }} +#define GOID_OFFSET 152 // +checkoffset runtime g.goid // func goid() int64 TEXT ·goid(SB),NOSPLIT,$0-8 diff --git a/pkg/goid/goid_arm64.s b/pkg/goid/goid_arm64.s index cadf5cf92..07b04a244 100644 --- a/pkg/goid/goid_arm64.s +++ b/pkg/goid/goid_arm64.s @@ -14,7 +14,7 @@ #include "textflag.h" -#define GOID_OFFSET {{ .import.runtime.g.goid.Offset }} +#define GOID_OFFSET 152 // +checkoffset runtime g.goid // func goid() int64 TEXT ·goid(SB),NOSPLIT,$0-8 diff --git a/pkg/hosttid/BUILD b/pkg/hosttid/BUILD index 7ae6a87c8..fdbb731fe 100644 --- a/pkg/hosttid/BUILD +++ b/pkg/hosttid/BUILD @@ -1,30 +1,13 @@ -load("//tools:arch.bzl", "arch_genrule", "select_arch") load("//tools:defs.bzl", "go_library", "go_test") -load("//tools/nogo:defs.bzl", "nogo_facts") package(licenses = ["notice"]) -nogo_facts( - name = "hosttid_impl", - srcs = ["hosttid.go"], - output = "hosttid_impl.s", - template = select_arch( - amd64 = "hosttid_amd64.s", - arm64 = "hosttid_arm64.s", - ), -) - -arch_genrule( - name = "hosttid_impl_arch", - src = ":hosttid_impl", - template = "hosttid_impl_%s.s", -) - go_library( name = "hosttid", srcs = [ "hosttid.go", - ":hosttid_impl_arch", + "hosttid_amd64.s", + "hosttid_arm64.s", ], visibility = ["//visibility:public"], ) diff --git a/pkg/hosttid/hosttid_amd64.s b/pkg/hosttid/hosttid_amd64.s index 7173ddf1b..46740285f 100644 --- a/pkg/hosttid/hosttid_amd64.s +++ b/pkg/hosttid/hosttid_amd64.s @@ -17,8 +17,8 @@ #include "textflag.h" -#define M_OFFSET {{ .import.runtime.g.m.Offset }} -#define PROCID_OFFSET {{ .import.runtime.m.procid.Offset }} +#define M_OFFSET 48 // +checkoffset runtime g.m +#define PROCID_OFFSET 72 // +checkoffset runtime m.procid TEXT ·Current(SB),NOSPLIT,$0-8 // procid is in getg().m.procid. diff --git a/pkg/hosttid/hosttid_arm64.s b/pkg/hosttid/hosttid_arm64.s index 22e23ef5b..b2292e352 100644 --- a/pkg/hosttid/hosttid_arm64.s +++ b/pkg/hosttid/hosttid_arm64.s @@ -17,8 +17,8 @@ #include "textflag.h" -#define M_OFFSET {{ .import.runtime.g.m.Offset }} -#define PROCID_OFFSET {{ .import.runtime.m.procid.Offset }} +#define M_OFFSET 48 // +checkoffset runtime g.m +#define PROCID_OFFSET 72 // +checkoffset runtime m.procid TEXT ·Current(SB),NOSPLIT,$0-8 // procid is in getg().m.procid. diff --git a/pkg/ring0/BUILD b/pkg/ring0/BUILD index c0c37a99b..85dccaf77 100644 --- a/pkg/ring0/BUILD +++ b/pkg/ring0/BUILD @@ -1,42 +1,7 @@ -load("//tools:arch.bzl", "arch_genrule", "select_arch") load("//tools:defs.bzl", "go_library") -load("//tools/nogo:defs.bzl", "nogo_facts") package(licenses = ["notice"]) -exports_files(glob(["*.go"])) - -nogo_facts( - name = "entry_impl", - srcs = [ - "aarch64.go", - "defs.go", - "defs_amd64.go", - "defs_arm64.go", - "entry_template.go", - "x86.go", - ], - output = "entry_impl.s", - template = select_arch( - amd64 = "entry_amd64.s", - arm64 = "entry_arm64.s", - ), - deps = [ - "//pkg/abi/linux", - "//pkg/cpuid", - "//pkg/hostarch", - "//pkg/ring0/pagetables", - "//pkg/sentry/arch", - "//pkg/sentry/arch/fpu", - ], -) - -arch_genrule( - name = "entry_impl_arch", - src = ":entry_impl", - template = "entry_impl_%s.s", -) - go_library( name = "ring0", srcs = [ @@ -45,7 +10,9 @@ go_library( "defs_amd64.go", "defs_arm64.go", "entry_amd64.go", + "entry_amd64.s", "entry_arm64.go", + "entry_arm64.s", "kernel.go", "kernel_amd64.go", "kernel_arm64.go", @@ -56,10 +23,10 @@ go_library( "lib_arm64.s", "ring0.go", "x86.go", - ":entry_impl_arch", ], visibility = ["//pkg/sentry:internal"], deps = [ + "//pkg/abi/linux", "//pkg/cpuid", "//pkg/hostarch", "//pkg/ring0/pagetables", diff --git a/pkg/ring0/entry_amd64.s b/pkg/ring0/entry_amd64.s index bf3f18053..e55a12659 100644 --- a/pkg/ring0/entry_amd64.s +++ b/pkg/ring0/entry_amd64.s @@ -16,78 +16,75 @@ #include "textflag.h" // CPU offsets. -#define CPU_REGISTERS {{ .CPU.registers.Offset }} -#define CPU_FPU_STATE {{ .CPU.floatingPointState.Offset }} -#define CPU_ERROR_CODE ({{ .CPU.CPUArchState.Offset }}+{{ .CPUArchState.errorCode.Offset }}) -#define CPU_ERROR_TYPE ({{ .CPU.CPUArchState.Offset }}+{{ .CPUArchState.errorType.Offset }}) -#define CPU_VECTOR ({{ .CPU.CPUArchState.Offset }}+{{ .CPUArchState.vector.Offset }}) -#define CPU_FAULT_ADDR ({{ .CPU.CPUArchState.Offset }}+{{ .CPUArchState.faultAddr.Offset }}) -#define CPU_ENTRY ({{ .CPU.CPUArchState.Offset }}+{{ .CPUArchState.kernelEntry.Offset }}) -#define CPU_HAS_XSAVE ({{ .CPU.CPUArchState.Offset }}+{{ .CPUArchState.hasXSAVE.Offset }}) -#define CPU_HAS_XSAVEOPT ({{ .CPU.CPUArchState.Offset }}+{{ .CPUArchState.hasXSAVEOPT.Offset }}) +#define CPU_REGISTERS 64 // +checkoffset . CPU.registers +#define CPU_FPU_STATE 280 // +checkoffset . CPU.floatingPointState +#define CPU_ARCH_STATE 16 // +checkoffset . CPU.CPUArchState +#define CPU_ERROR_CODE CPU_ARCH_STATE+0 // +checkoffset . CPUArchState.errorCode +#define CPU_ERROR_TYPE CPU_ARCH_STATE+8 // +checkoffset . CPUArchState.errorType +#define CPU_VECTOR CPU_ARCH_STATE+16 // +checkoffset . CPUArchState.vector +#define CPU_FAULT_ADDR CPU_ARCH_STATE+24 // +checkoffset . CPUArchState.faultAddr +#define CPU_ENTRY CPU_ARCH_STATE+32 // +checkoffset . CPUArchState.kernelEntry +#define CPU_HAS_XSAVE CPU_ARCH_STATE+40 // +checkoffset . CPUArchState.hasXSAVE +#define CPU_HAS_XSAVEOPT CPU_ARCH_STATE+41 // +checkoffset . CPUArchState.hasXSAVEOPT -{{ with .kernelEntry }} -#define ENTRY_SCRATCH0 {{ .scratch0.Offset }} -#define ENTRY_STACK_TOP {{ .stackTop.Offset }} -#define ENTRY_CPU_SELF {{ .cpuSelf.Offset }} -#define ENTRY_KERNEL_CR3 {{ .kernelCR3.Offset }} -{{ end }} +#define ENTRY_SCRATCH0 256 // +checkoffset . kernelEntry.scratch0 +#define ENTRY_STACK_TOP 264 // +checkoffset . kernelEntry.stackTop +#define ENTRY_CPU_SELF 272 // +checkoffset . kernelEntry.cpuSelf +#define ENTRY_KERNEL_CR3 280 // +checkoffset . kernelEntry.kernelCR3 // Bits. -#define _RFLAGS_IF {{ .Constants._RFLAGS_IF }} -#define _RFLAGS_IOPL0 {{ .Constants._RFLAGS_IOPL0 }} -#define _KERNEL_FLAGS {{ .Constants.KernelFlagsSet }} +#define _RFLAGS_IF 512 // +checkconst . _RFLAGS_IF +#define _RFLAGS_IOPL0 4096 // +checkconst . _RFLAGS_IOPL0 +#define _KERNEL_FLAGS 2 // +checkconst . KernelFlagsSet // Vectors. -#define DivideByZero {{ .Constants.DivideByZero }} -#define Debug {{ .Constants.Debug }} -#define NMI {{ .Constants.NMI }} -#define Breakpoint {{ .Constants.Breakpoint }} -#define Overflow {{ .Constants.Overflow }} -#define BoundRangeExceeded {{ .Constants.BoundRangeExceeded }} -#define InvalidOpcode {{ .Constants.InvalidOpcode }} -#define DeviceNotAvailable {{ .Constants.DeviceNotAvailable }} -#define DoubleFault {{ .Constants.DoubleFault }} -#define CoprocessorSegmentOverrun {{ .Constants.CoprocessorSegmentOverrun }} -#define InvalidTSS {{ .Constants.InvalidTSS }} -#define SegmentNotPresent {{ .Constants.SegmentNotPresent }} -#define StackSegmentFault {{ .Constants.StackSegmentFault }} -#define GeneralProtectionFault {{ .Constants.GeneralProtectionFault }} -#define PageFault {{ .Constants.PageFault }} -#define X87FloatingPointException {{ .Constants.X87FloatingPointException }} -#define AlignmentCheck {{ .Constants.AlignmentCheck }} -#define MachineCheck {{ .Constants.MachineCheck }} -#define SIMDFloatingPointException {{ .Constants.SIMDFloatingPointException }} -#define VirtualizationException {{ .Constants.VirtualizationException }} -#define SecurityException {{ .Constants.SecurityException }} -#define SyscallInt80 {{ .Constants.SyscallInt80 }} -#define Syscall {{ .Constants.Syscall }} +#define DivideByZero 0 // +checkconst . DivideByZero +#define Debug 1 // +checkconst . Debug +#define NMI 2 // +checkconst . NMI +#define Breakpoint 3 // +checkconst . Breakpoint +#define Overflow 4 // +checkconst . Overflow +#define BoundRangeExceeded 5 // +checkconst . BoundRangeExceeded +#define InvalidOpcode 6 // +checkconst . InvalidOpcode +#define DeviceNotAvailable 7 // +checkconst . DeviceNotAvailable +#define DoubleFault 8 // +checkconst . DoubleFault +#define CoprocessorSegmentOverrun 9 // +checkconst . CoprocessorSegmentOverrun +#define InvalidTSS 10 // +checkconst . InvalidTSS +#define SegmentNotPresent 11 // +checkconst . SegmentNotPresent +#define StackSegmentFault 12 // +checkconst . StackSegmentFault +#define GeneralProtectionFault 13 // +checkconst . GeneralProtectionFault +#define PageFault 14 // +checkconst . PageFault +#define X87FloatingPointException 16 // +checkconst . X87FloatingPointException +#define AlignmentCheck 17 // +checkconst . AlignmentCheck +#define MachineCheck 18 // +checkconst . MachineCheck +#define SIMDFloatingPointException 19 // +checkconst . SIMDFloatingPointException +#define VirtualizationException 20 // +checkconst . VirtualizationException +#define SecurityException 30 // +checkconst . SecurityException +#define SyscallInt80 128 // +checkconst . SyscallInt80 +#define Syscall 256 // +checkconst . Syscall -{{ with .import.linux.PtraceRegs }} -#define PTRACE_R15 {{ .R15.Offset }} -#define PTRACE_R14 {{ .R14.Offset }} -#define PTRACE_R13 {{ .R13.Offset }} -#define PTRACE_R12 {{ .R12.Offset }} -#define PTRACE_RBP {{ .Rbp.Offset }} -#define PTRACE_RBX {{ .Rbx.Offset }} -#define PTRACE_R11 {{ .R11.Offset }} -#define PTRACE_R10 {{ .R10.Offset }} -#define PTRACE_R9 {{ .R9.Offset }} -#define PTRACE_R8 {{ .R8.Offset }} -#define PTRACE_RAX {{ .Rax.Offset }} -#define PTRACE_RCX {{ .Rcx.Offset }} -#define PTRACE_RDX {{ .Rdx.Offset }} -#define PTRACE_RSI {{ .Rsi.Offset }} -#define PTRACE_RDI {{ .Rdi.Offset }} -#define PTRACE_ORIGRAX {{ .Orig_rax.Offset }} -#define PTRACE_RIP {{ .Rip.Offset }} -#define PTRACE_CS {{ .Cs.Offset }} -#define PTRACE_FLAGS {{ .Eflags.Offset }} -#define PTRACE_RSP {{ .Rsp.Offset }} -#define PTRACE_SS {{ .Ss.Offset }} -#define PTRACE_FS_BASE {{ .Fs_base.Offset }} -#define PTRACE_GS_BASE {{ .Gs_base.Offset }} -{{ end }} +#define PTRACE_R15 0 // +checkoffset linux PtraceRegs.R15 +#define PTRACE_R14 8 // +checkoffset linux PtraceRegs.R14 +#define PTRACE_R13 16 // +checkoffset linux PtraceRegs.R13 +#define PTRACE_R12 24 // +checkoffset linux PtraceRegs.R12 +#define PTRACE_RBP 32 // +checkoffset linux PtraceRegs.Rbp +#define PTRACE_RBX 40 // +checkoffset linux PtraceRegs.Rbx +#define PTRACE_R11 48 // +checkoffset linux PtraceRegs.R11 +#define PTRACE_R10 56 // +checkoffset linux PtraceRegs.R10 +#define PTRACE_R9 64 // +checkoffset linux PtraceRegs.R9 +#define PTRACE_R8 72 // +checkoffset linux PtraceRegs.R8 +#define PTRACE_RAX 80 // +checkoffset linux PtraceRegs.Rax +#define PTRACE_RCX 88 // +checkoffset linux PtraceRegs.Rcx +#define PTRACE_RDX 96 // +checkoffset linux PtraceRegs.Rdx +#define PTRACE_RSI 104 // +checkoffset linux PtraceRegs.Rsi +#define PTRACE_RDI 112 // +checkoffset linux PtraceRegs.Rdi +#define PTRACE_ORIGRAX 120 // +checkoffset linux PtraceRegs.Orig_rax +#define PTRACE_RIP 128 // +checkoffset linux PtraceRegs.Rip +#define PTRACE_CS 136 // +checkoffset linux PtraceRegs.Cs +#define PTRACE_FLAGS 144 // +checkoffset linux PtraceRegs.Eflags +#define PTRACE_RSP 152 // +checkoffset linux PtraceRegs.Rsp +#define PTRACE_SS 160 // +checkoffset linux PtraceRegs.Ss +#define PTRACE_FS_BASE 168 // +checkoffset linux PtraceRegs.Fs_base +#define PTRACE_GS_BASE 176 // +checkoffset linux PtraceRegs.Gs_base // Saves a register set. // diff --git a/pkg/ring0/entry_arm64.s b/pkg/ring0/entry_arm64.s index 3e8526fdc..421465f70 100644 --- a/pkg/ring0/entry_arm64.s +++ b/pkg/ring0/entry_arm64.s @@ -15,94 +15,93 @@ #include "funcdata.h" #include "textflag.h" -#define CPU_SELF {{ .CPU.self.Offset }} -#define CPU_REGISTERS {{ .CPU.registers.Offset }} -#define CPU_STACK_TOP ({{ .CPU.CPUArchState.Offset }}+{{ .CPUArchState.stack.Offset }} + {{ .CPUArchState.stack.Size }}) -#define CPU_ERROR_CODE ({{ .CPU.CPUArchState.Offset }}+{{ .CPUArchState.errorCode.Offset }}) -#define CPU_ERROR_TYPE ({{ .CPU.CPUArchState.Offset }}+{{ .CPUArchState.errorType.Offset }}) -#define CPU_FAULT_ADDR ({{ .CPU.CPUArchState.Offset }}+{{ .CPUArchState.faultAddr.Offset }}) -#define CPU_FPSTATE_EL0 ({{ .CPU.CPUArchState.Offset }}+{{ .CPUArchState.el0Fp.Offset }}) -#define CPU_TTBR0_KVM ({{ .CPU.CPUArchState.Offset }}+{{ .CPUArchState.ttbr0Kvm.Offset }}) -#define CPU_TTBR0_APP ({{ .CPU.CPUArchState.Offset }}+{{ .CPUArchState.ttbr0App.Offset }}) -#define CPU_VECTOR_CODE ({{ .CPU.CPUArchState.Offset }}+{{ .CPUArchState.vecCode.Offset }}) -#define CPU_APP_ADDR ({{ .CPU.CPUArchState.Offset }}+{{ .CPUArchState.appAddr.Offset }}) -#define CPU_LAZY_VFP ({{ .CPU.CPUArchState.Offset }}+{{ .CPUArchState.lazyVFP.Offset }}) -#define CPU_APP_ASID ({{ .CPU.CPUArchState.Offset }}+{{ .CPUArchState.appASID.Offset }}) +#define CPU_SELF 0 // +checkoffset . CPU.self +#define CPU_REGISTERS 224 // +checkoffset . CPU.registers +#define CPU_ARCH_STATE 16 // +checkoffset . CPU.CPUArchState +#define CPU_STACK_BOTTOM CPU_ARCH_STATE+0 // +checkoffset . CPUArchState.stack +#define CPU_STACK_TOP CPU_STACK_BOTTOM+128 // +checksize . CPUArchState.stack +#define CPU_ERROR_CODE CPU_ARCH_STATE+128 // +checkoffset . CPUArchState.errorCode +#define CPU_ERROR_TYPE CPU_ARCH_STATE+136 // +checkoffset . CPUArchState.errorType +#define CPU_FAULT_ADDR CPU_ARCH_STATE+144 // +checkoffset . CPUArchState.faultAddr +#define CPU_FPSTATE_EL0 CPU_ARCH_STATE+152 // +checkoffset . CPUArchState.el0Fp +#define CPU_TTBR0_KVM CPU_ARCH_STATE+160 // +checkoffset . CPUArchState.ttbr0Kvm +#define CPU_TTBR0_APP CPU_ARCH_STATE+168 // +checkoffset . CPUArchState.ttbr0App +#define CPU_VECTOR_CODE CPU_ARCH_STATE+176 // +checkoffset . CPUArchState.vecCode +#define CPU_APP_ADDR CPU_ARCH_STATE+184 // +checkoffset . CPUArchState.appAddr +#define CPU_LAZY_VFP CPU_ARCH_STATE+192 // +checkoffset . CPUArchState.lazyVFP +#define CPU_APP_ASID CPU_ARCH_STATE+200 // +checkoffset . CPUArchState.appASID // Bits. -#define _KERNEL_FLAGS {{ .Constants.KernelFlagsSet }} +#define _KERNEL_FLAGS 965 // +checkconst . KernelFlagsSet // Vectors. -#define El1Sync {{ .Constants.El1Sync }} -#define El1Irq {{ .Constants.El1Irq }} -#define El1Fiq {{ .Constants.El1Fiq }} -#define El1Err {{ .Constants.El1Err }} -#define El0Sync {{ .Constants.El0Sync }} -#define El0Irq {{ .Constants.El0Irq }} -#define El0Fiq {{ .Constants.El0Fiq }} -#define El0Err {{ .Constants.El0Err }} -#define El1SyncDa {{ .Constants.El1SyncDa }} -#define El1SyncIa {{ .Constants.El1SyncIa }} -#define El1SyncSpPc {{ .Constants.El1SyncSpPc }} -#define El1SyncUndef {{ .Constants.El1SyncUndef }} -#define El1SyncDbg {{ .Constants.El1SyncDbg }} -#define El1SyncInv {{ .Constants.El1SyncInv }} -#define El0SyncSVC {{ .Constants.El0SyncSVC }} -#define El0SyncDa {{ .Constants.El0SyncDa }} -#define El0SyncIa {{ .Constants.El0SyncIa }} -#define El0SyncFpsimdAcc {{ .Constants.El0SyncFpsimdAcc }} -#define El0SyncSveAcc {{ .Constants.El0SyncSveAcc }} -#define El0SyncFpsimdExc {{ .Constants.El0SyncFpsimdExc }} -#define El0SyncSys {{ .Constants.El0SyncSys }} -#define El0SyncSpPc {{ .Constants.El0SyncSpPc }} -#define El0SyncUndef {{ .Constants.El0SyncUndef }} -#define El0SyncDbg {{ .Constants.El0SyncDbg }} -#define El0SyncWfx {{ .Constants.El0SyncWfx }} -#define El0SyncInv {{ .Constants.El0SyncInv }} -#define El0ErrNMI {{ .Constants.El0ErrNMI }} -#define PageFault {{ .Constants.PageFault }} -#define Syscall {{ .Constants.Syscall }} -#define VirtualizationException {{ .Constants.VirtualizationException }} +#define El1Sync 4 // +checkconst . El1Sync +#define El1Irq 5 // +checkconst . El1Irq +#define El1Fiq 6 // +checkconst . El1Fiq +#define El1Err 7 // +checkconst . El1Err +#define El0Sync 8 // +checkconst . El0Sync +#define El0Irq 9 // +checkconst . El0Irq +#define El0Fiq 10 // +checkconst . El0Fiq +#define El0Err 11 // +checkconst . El0Err +#define El1SyncDa 16 // +checkconst . El1SyncDa +#define El1SyncIa 17 // +checkconst . El1SyncIa +#define El1SyncSpPc 18 // +checkconst . El1SyncSpPc +#define El1SyncUndef 19 // +checkconst . El1SyncUndef +#define El1SyncDbg 20 // +checkconst . El1SyncDbg +#define El1SyncInv 21 // +checkconst . El1SyncInv +#define El0SyncSVC 22 // +checkconst . El0SyncSVC +#define El0SyncDa 23 // +checkconst . El0SyncDa +#define El0SyncIa 24 // +checkconst . El0SyncIa +#define El0SyncFpsimdAcc 25 // +checkconst . El0SyncFpsimdAcc +#define El0SyncSveAcc 26 // +checkconst . El0SyncSveAcc +#define El0SyncFpsimdExc 27 // +checkconst . El0SyncFpsimdExc +#define El0SyncSys 28 // +checkconst . El0SyncSys +#define El0SyncSpPc 29 // +checkconst . El0SyncSpPc +#define El0SyncUndef 30 // +checkconst . El0SyncUndef +#define El0SyncDbg 31 // +checkconst . El0SyncDbg +#define El0SyncWfx 32 // +checkconst . El0SyncWfx +#define El0SyncInv 33 // +checkconst . El0SyncInv +#define El0ErrNMI 34 // +checkconst . El0ErrNMI +#define PageFault 23 // +checkconst . PageFault +#define Syscall 22 // +checkconst . Syscall +#define VirtualizationException 35 // +checkconst . VirtualizationException -{{ with .import.linux.PtraceRegs }} -#define PTRACE_R0 ({{ .Regs.Offset }} + 0*8) -#define PTRACE_R1 ({{ .Regs.Offset }} + 1*8) -#define PTRACE_R2 ({{ .Regs.Offset }} + 2*8) -#define PTRACE_R3 ({{ .Regs.Offset }} + 3*8) -#define PTRACE_R4 ({{ .Regs.Offset }} + 4*8) -#define PTRACE_R5 ({{ .Regs.Offset }} + 5*8) -#define PTRACE_R6 ({{ .Regs.Offset }} + 6*8) -#define PTRACE_R7 ({{ .Regs.Offset }} + 7*8) -#define PTRACE_R8 ({{ .Regs.Offset }} + 8*8) -#define PTRACE_R9 ({{ .Regs.Offset }} + 9*8) -#define PTRACE_R10 ({{ .Regs.Offset }} + 10*8) -#define PTRACE_R11 ({{ .Regs.Offset }} + 11*8) -#define PTRACE_R12 ({{ .Regs.Offset }} + 12*8) -#define PTRACE_R13 ({{ .Regs.Offset }} + 13*8) -#define PTRACE_R14 ({{ .Regs.Offset }} + 14*8) -#define PTRACE_R15 ({{ .Regs.Offset }} + 15*8) -#define PTRACE_R16 ({{ .Regs.Offset }} + 16*8) -#define PTRACE_R17 ({{ .Regs.Offset }} + 17*8) -#define PTRACE_R18 ({{ .Regs.Offset }} + 18*8) -#define PTRACE_R19 ({{ .Regs.Offset }} + 19*8) -#define PTRACE_R20 ({{ .Regs.Offset }} + 20*8) -#define PTRACE_R21 ({{ .Regs.Offset }} + 21*8) -#define PTRACE_R22 ({{ .Regs.Offset }} + 22*8) -#define PTRACE_R23 ({{ .Regs.Offset }} + 23*8) -#define PTRACE_R24 ({{ .Regs.Offset }} + 24*8) -#define PTRACE_R25 ({{ .Regs.Offset }} + 25*8) -#define PTRACE_R26 ({{ .Regs.Offset }} + 26*8) -#define PTRACE_R27 ({{ .Regs.Offset }} + 27*8) -#define PTRACE_R28 ({{ .Regs.Offset }} + 28*8) -#define PTRACE_R29 ({{ .Regs.Offset }} + 29*8) -#define PTRACE_R30 ({{ .Regs.Offset }} + 30*8) -#define PTRACE_SP {{ .Sp.Offset }} -#define PTRACE_PC {{ .Pc.Offset }} -#define PTRACE_PSTATE {{ .Pstate.Offset }} -{{ end }} -{{ with .import.arch.Registers }} -#define PTRACE_TLS {{ .TPIDR_EL0.Offset }} -{{ end }} +#define PTRACE_REGS 0 // +checkoffset linux PtraceRegs.Regs +#define PTRACE_R0 (PTRACE_REGS + 0*8) +#define PTRACE_R1 (PTRACE_REGS + 1*8) +#define PTRACE_R2 (PTRACE_REGS + 2*8) +#define PTRACE_R3 (PTRACE_REGS + 3*8) +#define PTRACE_R4 (PTRACE_REGS + 4*8) +#define PTRACE_R5 (PTRACE_REGS + 5*8) +#define PTRACE_R6 (PTRACE_REGS + 6*8) +#define PTRACE_R7 (PTRACE_REGS + 7*8) +#define PTRACE_R8 (PTRACE_REGS + 8*8) +#define PTRACE_R9 (PTRACE_REGS + 9*8) +#define PTRACE_R10 (PTRACE_REGS + 10*8) +#define PTRACE_R11 (PTRACE_REGS + 11*8) +#define PTRACE_R12 (PTRACE_REGS + 12*8) +#define PTRACE_R13 (PTRACE_REGS + 13*8) +#define PTRACE_R14 (PTRACE_REGS + 14*8) +#define PTRACE_R15 (PTRACE_REGS + 15*8) +#define PTRACE_R16 (PTRACE_REGS + 16*8) +#define PTRACE_R17 (PTRACE_REGS + 17*8) +#define PTRACE_R18 (PTRACE_REGS + 18*8) +#define PTRACE_R19 (PTRACE_REGS + 19*8) +#define PTRACE_R20 (PTRACE_REGS + 20*8) +#define PTRACE_R21 (PTRACE_REGS + 21*8) +#define PTRACE_R22 (PTRACE_REGS + 22*8) +#define PTRACE_R23 (PTRACE_REGS + 23*8) +#define PTRACE_R24 (PTRACE_REGS + 24*8) +#define PTRACE_R25 (PTRACE_REGS + 25*8) +#define PTRACE_R26 (PTRACE_REGS + 26*8) +#define PTRACE_R27 (PTRACE_REGS + 27*8) +#define PTRACE_R28 (PTRACE_REGS + 28*8) +#define PTRACE_R29 (PTRACE_REGS + 29*8) +#define PTRACE_R30 (PTRACE_REGS + 30*8) +#define PTRACE_SP 248 // +checkoffset linux PtraceRegs.Sp +#define PTRACE_PC 256 // +checkoffset linux PtraceRegs.Pc +#define PTRACE_PSTATE 264 // +checkoffset linux PtraceRegs.Pstate +#define PTRACE_TLS 272 // +checkoffset arch Registers.TPIDR_EL0 // Saves a register set. // diff --git a/pkg/ring0/entry_template.go b/pkg/ring0/entry_template.go deleted file mode 100644 index c51abf586..000000000 --- a/pkg/ring0/entry_template.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ring0 - -import ( - // Used for template generation. - _ "gvisor.dev/gvisor/pkg/abi/linux" -) diff --git a/pkg/ring0/ring0.go b/pkg/ring0/ring0.go index cdeb1b43a..0dda29a0e 100644 --- a/pkg/ring0/ring0.go +++ b/pkg/ring0/ring0.go @@ -14,3 +14,8 @@ // Package ring0 provides basic operating system-level stubs. package ring0 + +import ( + // Required for facts checks. + _ "gvisor.dev/gvisor/pkg/abi/linux" +) diff --git a/pkg/sentry/platform/kvm/BUILD b/pkg/sentry/platform/kvm/BUILD index ced668564..0e96465cf 100644 --- a/pkg/sentry/platform/kvm/BUILD +++ b/pkg/sentry/platform/kvm/BUILD @@ -1,7 +1,5 @@ -load("//tools:arch.bzl", "arch_genrule", "select_arch") load("//tools:defs.bzl", "go_library", "go_test") load("//tools/go_generics:defs.bzl", "go_template_instance") -load("//tools/nogo:defs.bzl", "nogo_facts") package(licenses = ["notice"]) @@ -16,37 +14,6 @@ go_template_instance( }, ) -nogo_facts( - name = "bluepill_impl", - srcs = [ - "//pkg/ring0:aarch64.go", - "//pkg/ring0:defs.go", - "//pkg/ring0:defs_amd64.go", - "//pkg/ring0:defs_arm64.go", - "//pkg/ring0:entry_template.go", - "//pkg/ring0:x86.go", - ], - output = "bluepill_impl.s", - template = select_arch( - amd64 = "bluepill_amd64.s", - arm64 = "bluepill_arm64.s", - ), - deps = [ - "//pkg/abi/linux", - "//pkg/cpuid", - "//pkg/hostarch", - "//pkg/ring0/pagetables", - "//pkg/sentry/arch", - "//pkg/sentry/arch/fpu", - ], -) - -arch_genrule( - name = "bluepill_impl_arch", - src = ":bluepill_impl", - template = "bluepill_impl_%s.s", -) - go_library( name = "kvm", srcs = [ @@ -57,8 +24,10 @@ go_library( "bluepill.go", "bluepill_allocator.go", "bluepill_amd64.go", + "bluepill_amd64.s", "bluepill_amd64_unsafe.go", "bluepill_arm64.go", + "bluepill_arm64.s", "bluepill_arm64_unsafe.go", "bluepill_fault.go", "bluepill_unsafe.go", @@ -84,7 +53,6 @@ go_library( "physical_map_amd64.go", "physical_map_arm64.go", "virtual_map.go", - ":bluepill_impl_arch", ], visibility = ["//pkg/sentry:internal"], deps = [ diff --git a/pkg/sentry/platform/kvm/bluepill_amd64.s b/pkg/sentry/platform/kvm/bluepill_amd64.s index 450ccf5de..a6309e14b 100644 --- a/pkg/sentry/platform/kvm/bluepill_amd64.s +++ b/pkg/sentry/platform/kvm/bluepill_amd64.s @@ -22,7 +22,7 @@ // ENTRY_CPU_SELF is the location of the CPU in the entry struct. // // This is sourced from ring0. -#define ENTRY_CPU_SELF {{ .kernelEntry.cpuSelf.Offset }} +#define ENTRY_CPU_SELF 272 // +checkoffset ring0 kernelEntry.cpuSelf // Context offsets. // diff --git a/pkg/sentry/platform/systrap/BUILD b/pkg/sentry/platform/systrap/BUILD index ef8248ac1..8eee1b304 100644 --- a/pkg/sentry/platform/systrap/BUILD +++ b/pkg/sentry/platform/systrap/BUILD @@ -1,35 +1,8 @@ -load("//tools:arch.bzl", "arch_genrule", "select_arch") load("//tools:defs.bzl", "go_library") -load("//tools/nogo:defs.bzl", "nogo_facts") load("//tools/go_generics:defs.bzl", "go_template_instance") package(licenses = ["notice"]) -nogo_facts( - name = "stub_impl", - srcs = [ - "stub_defs.go", - "syscall_thread_defs.go", - ], - output = "stub_impl.s", - template = select_arch( - amd64 = "stub_amd64.s", - arm64 = "stub_arm64.s", - ), - deps = [ - "//pkg/abi/linux", - "//pkg/atomicbitops", - "//pkg/hostarch", - "@org_golang_x_sys//unix:go_default_library", - ], -) - -arch_genrule( - name = "stub_impl_arch", - src = ":stub_impl", - template = "stub_impl_%s.s", -) - go_template_instance( name = "subprocess_list", out = "subprocess_list.go", @@ -50,6 +23,8 @@ go_library( "filters_arm64.go", "lib_amd64.s", "lib_arm64.s", + "stub_amd64.s", + "stub_arm64.s", "stub_defs.go", "stub_unsafe.go", "subprocess.go", @@ -76,7 +51,6 @@ go_library( "systrap_arm64.go", "systrap_arm64_unsafe.go", "systrap_unsafe.go", - ":stub_impl_arch", ], visibility = ["//:sandbox"], deps = [ diff --git a/pkg/sentry/platform/systrap/stub_amd64.s b/pkg/sentry/platform/systrap/stub_amd64.s index 2d5a6820e..ea36cd5bc 100644 --- a/pkg/sentry/platform/systrap/stub_amd64.s +++ b/pkg/sentry/platform/systrap/stub_amd64.s @@ -15,35 +15,36 @@ #include "funcdata.h" #include "textflag.h" -#define SYS_GETPID {{ .import.unix.Constants.SYS_GETPID }} -#define SYS_EXIT {{ .import.unix.Constants.SYS_EXIT }} -#define SYS_KILL {{ .import.unix.Constants.SYS_KILL }} -#define SYS_GETPPID {{ .import.unix.Constants.SYS_GETPPID }} -#define SIGKILL {{ .import.unix.Constants.SIGKILL }} -#define SIGSTOP {{ .import.unix.Constants.SIGSTOP }} -#define SYS_PRCTL {{ .import.unix.Constants.SYS_PRCTL }} -#define PR_SET_PDEATHSIG {{ .import.unix.Constants.PR_SET_PDEATHSIG }} +#define SYS_GETPID 39 // +checkconst unix SYS_GETPID +#define SYS_EXIT 60 // +checkconst unix SYS_EXIT +#define SYS_KILL 62 // +checkconst unix SYS_KILL +#define SYS_GETPPID 110 // +checkconst unix SYS_GETPPID +#define SIGKILL 9 // +checkconst unix SIGKILL +#define SIGSTOP 19 // +checkconst unix SIGSTOP +#define SYS_PRCTL 157 // +checkconst unix SYS_PRCTL +#define PR_SET_PDEATHSIG 1 // +checkconst unix PR_SET_PDEATHSIG -#define SYS_FUTEX {{ .import.unix.Constants.SYS_FUTEX }} -#define FUTEX_WAKE {{ .import.linux.Constants.FUTEX_WAKE }} -#define FUTEX_WAIT {{ .import.linux.Constants.FUTEX_WAIT }} +#define SYS_FUTEX 202 // +checkconst unix SYS_FUTEX +#define FUTEX_WAKE 1 // +checkconst linux FUTEX_WAKE +#define FUTEX_WAIT 0 // +checkconst linux FUTEX_WAIT -#define NEW_STUB {{ .Constants._NEW_STUB }} -#define RUN_SYSCALL_LOOP {{ .Constants._RUN_SYSCALL_LOOP }} +#define NEW_STUB 1 // +checkconst . _NEW_STUB +#define RUN_SYSCALL_LOOP 5 // +checkconst . _RUN_SYSCALL_LOOP // syscallSentryMessage offsets. -#define SENTRY_MESSAGE_STATE {{ .syscallSentryMessage.state.Offset }} -#define SENTRY_MESSAGE_SYSNO {{ .syscallSentryMessage.sysno.Offset }} -#define SENTRY_MESSAGE_ARG0 ({{ .syscallSentryMessage.args.Offset }} + 0*8) -#define SENTRY_MESSAGE_ARG1 ({{ .syscallSentryMessage.args.Offset }} + 1*8) -#define SENTRY_MESSAGE_ARG2 ({{ .syscallSentryMessage.args.Offset }} + 2*8) -#define SENTRY_MESSAGE_ARG3 ({{ .syscallSentryMessage.args.Offset }} + 3*8) -#define SENTRY_MESSAGE_ARG4 ({{ .syscallSentryMessage.args.Offset }} + 4*8) -#define SENTRY_MESSAGE_ARG5 ({{ .syscallSentryMessage.args.Offset }} + 5*8) +#define SENTRY_MESSAGE_STATE 0 // +checkoffset . syscallSentryMessage.state +#define SENTRY_MESSAGE_SYSNO 8 // +checkoffset . syscallSentryMessage.sysno +#define SENTRY_MESSAGE_ARGS 16 // +checkoffset . syscallSentryMessage.args +#define SENTRY_MESSAGE_ARG0 (SENTRY_MESSAGE_ARGS + 0*8) +#define SENTRY_MESSAGE_ARG1 (SENTRY_MESSAGE_ARGS + 1*8) +#define SENTRY_MESSAGE_ARG2 (SENTRY_MESSAGE_ARGS + 2*8) +#define SENTRY_MESSAGE_ARG3 (SENTRY_MESSAGE_ARGS + 3*8) +#define SENTRY_MESSAGE_ARG4 (SENTRY_MESSAGE_ARGS + 4*8) +#define SENTRY_MESSAGE_ARG5 (SENTRY_MESSAGE_ARGS + 5*8) // syscallStubMessage offsets. -#define STUB_MESSAGE_OFFSET {{ .Constants.syscallStubMessageOffset }} -#define STUB_MESSAGE_RET {{ .syscallStubMessage.ret.Offset }} +#define STUB_MESSAGE_OFFSET 4096 // +checkconst . syscallStubMessageOffset +#define STUB_MESSAGE_RET 0 // +checkoffset . syscallStubMessage.ret // initStubProcess bootstraps the child and sends itself SIGSTOP to wait for attach. // diff --git a/pkg/sentry/platform/systrap/stub_arm64.s b/pkg/sentry/platform/systrap/stub_arm64.s index 916bd1953..62c583d50 100644 --- a/pkg/sentry/platform/systrap/stub_arm64.s +++ b/pkg/sentry/platform/systrap/stub_arm64.s @@ -15,35 +15,36 @@ #include "funcdata.h" #include "textflag.h" -#define SYS_GETPID {{ .import.unix.Constants.SYS_GETPID }} -#define SYS_EXIT {{ .import.unix.Constants.SYS_EXIT }} -#define SYS_KILL {{ .import.unix.Constants.SYS_KILL }} -#define SYS_GETPPID {{ .import.unix.Constants.SYS_GETPPID }} -#define SIGKILL {{ .import.unix.Constants.SIGKILL }} -#define SIGSTOP {{ .import.unix.Constants.SIGSTOP }} -#define SYS_PRCTL {{ .import.unix.Constants.SYS_PRCTL }} -#define PR_SET_PDEATHSIG {{ .import.unix.Constants.PR_SET_PDEATHSIG }} +#define SYS_GETPID 172 // +checkconst unix SYS_GETPID +#define SYS_EXIT 93 // +checkconst unix SYS_EXIT +#define SYS_KILL 129 // +checkconst unix SYS_KILL +#define SYS_GETPPID 173 // +checkconst unix SYS_GETPPID +#define SIGKILL 9 // +checkconst unix SIGKILL +#define SIGSTOP 19 // +checkconst unix SIGSTOP +#define SYS_PRCTL 167 // +checkconst unix SYS_PRCTL +#define PR_SET_PDEATHSIG 1 // +checkconst unix PR_SET_PDEATHSIG -#define SYS_FUTEX {{ .import.unix.Constants.SYS_FUTEX }} -#define FUTEX_WAKE {{ .import.linux.Constants.FUTEX_WAKE }} -#define FUTEX_WAIT {{ .import.linux.Constants.FUTEX_WAIT }} +#define SYS_FUTEX 98 // +checkconst unix SYS_FUTEX +#define FUTEX_WAKE 1 // +checkconst linux FUTEX_WAKE +#define FUTEX_WAIT 0 // +checkconst linux FUTEX_WAIT -#define NEW_STUB {{ .Constants._NEW_STUB }} -#define RUN_SYSCALL_LOOP {{ .Constants._RUN_SYSCALL_LOOP }} +#define NEW_STUB 1 // +checkconst . _NEW_STUB +#define RUN_SYSCALL_LOOP 5 // +checkconst . _RUN_SYSCALL_LOOP // syscallSentryMessage offsets. -#define SENTRY_MESSAGE_STATE {{ .syscallSentryMessage.state.Offset }} -#define SENTRY_MESSAGE_SYSNO {{ .syscallSentryMessage.sysno.Offset }} -#define SENTRY_MESSAGE_ARG0 ({{ .syscallSentryMessage.args.Offset }} + 0*8) -#define SENTRY_MESSAGE_ARG1 ({{ .syscallSentryMessage.args.Offset }} + 1*8) -#define SENTRY_MESSAGE_ARG2 ({{ .syscallSentryMessage.args.Offset }} + 2*8) -#define SENTRY_MESSAGE_ARG3 ({{ .syscallSentryMessage.args.Offset }} + 3*8) -#define SENTRY_MESSAGE_ARG4 ({{ .syscallSentryMessage.args.Offset }} + 4*8) -#define SENTRY_MESSAGE_ARG5 ({{ .syscallSentryMessage.args.Offset }} + 5*8) +#define SENTRY_MESSAGE_STATE 0 // +checkoffset . syscallSentryMessage.state +#define SENTRY_MESSAGE_SYSNO 8 // +checkoffset . syscallSentryMessage.sysno +#define SENTRY_MESSAGE_ARGS 16 // +checkoffset . syscallSentryMessage.args +#define SENTRY_MESSAGE_ARG0 (SENTRY_MESSAGE_ARGS + 0*8) +#define SENTRY_MESSAGE_ARG1 (SENTRY_MESSAGE_ARGS + 1*8) +#define SENTRY_MESSAGE_ARG2 (SENTRY_MESSAGE_ARGS + 2*8) +#define SENTRY_MESSAGE_ARG3 (SENTRY_MESSAGE_ARGS + 3*8) +#define SENTRY_MESSAGE_ARG4 (SENTRY_MESSAGE_ARGS + 4*8) +#define SENTRY_MESSAGE_ARG5 (SENTRY_MESSAGE_ARGS + 5*8) // syscallStubMessage offsets. -#define STUB_MESSAGE_OFFSET {{ .Constants.syscallStubMessageOffset }} -#define STUB_MESSAGE_RET {{ .syscallStubMessage.ret.Offset }} +#define STUB_MESSAGE_OFFSET 4096 // +checkconst . syscallStubMessageOffset +#define STUB_MESSAGE_RET 0 // +checkoffset . syscallStubMessage.ret // initStubProcess bootstraps the child and sends itself SIGSTOP to wait for attach. // diff --git a/pkg/sync/BUILD b/pkg/sync/BUILD index 47847ac6b..f35dfa254 100644 --- a/pkg/sync/BUILD +++ b/pkg/sync/BUILD @@ -1,42 +1,10 @@ -load("//tools:arch.bzl", "arch_genrule", "select_arch") load("//tools:defs.bzl", "go_library", "go_test") -load("//tools/nogo:defs.bzl", "nogo_facts") package( default_visibility = ["//:sandbox"], licenses = ["notice"], ) -nogo_facts( - name = "runtime_spinning_impl", - srcs = ["runtime.go"], - output = "runtime_spinning_impl.s", - template = select_arch( - amd64 = "runtime_spinning_amd64.s", - arm64 = "runtime_spinning_other.s", - ), -) - -# For arm64 (or any !amd64), this will generate runtime_spinning_impl_arm64.s, -# which is a copy of the (empty) runtime_spinning_other.s. -# -# On the go branch, only amd64 and arm64 will have have files, other arches -# won't select any of these files. That is fine because the contents only -# matter for amd64 anyway. -arch_genrule( - name = "runtime_spinning_impl_arch", - src = ":runtime_spinning_impl", - template = "runtime_spinning_impl_%s.s", -) - -# Architecture-independent constants. -nogo_facts( - name = "runtime_constants_impl", - srcs = ["runtime.go"], - output = "runtime_constants_impl.go", - template = "runtime_constants.tmpl", -) - go_library( name = "sync", srcs = [ @@ -52,14 +20,16 @@ go_library( "race_amd64.s", "race_arm64.s", "race_unsafe.go", + "runtime.go", "runtime_amd64.go", + "runtime_constants.go", "runtime_other.go", + "runtime_spinning_amd64.s", + "runtime_spinning_other.s", "runtime_unsafe.go", "rwmutex_unsafe.go", "seqcount.go", "sync.go", - ":runtime_constants_impl", - ":runtime_spinning_impl_arch", ], marshal = False, stateify = False, diff --git a/pkg/sync/runtime_constants.tmpl b/pkg/sync/runtime_constants.go similarity index 61% rename from pkg/sync/runtime_constants.tmpl rename to pkg/sync/runtime_constants.go index f843d7fca..9a5a47a82 100644 --- a/pkg/sync/runtime_constants.tmpl +++ b/pkg/sync/runtime_constants.go @@ -16,14 +16,14 @@ package sync // Values for the reason argument to gopark, from Go's src/runtime/runtime2.go. const ( - WaitReasonSelect uint8 = {{ .import.runtime.Constants.waitReasonSelect }} - WaitReasonChanReceive uint8 = {{ .import.runtime.Constants.waitReasonChanReceive }} - WaitReasonSemacquire uint8 = {{ .import.runtime.Constants.waitReasonSemacquire }} + WaitReasonSelect uint8 = 9 // +checkconst runtime waitReasonSelect + WaitReasonChanReceive uint8 = 14 // +checkconst runtime waitReasonChanReceive + WaitReasonSemacquire uint8 = 18 // +checkconst runtime waitReasonSemacquire ) // Values for the traceEv argument to gopark, from Go's src/runtime/trace.go. const ( - TraceEvGoBlockRecv byte = {{ .import.runtime.Constants.traceEvGoBlockRecv }} - TraceEvGoBlockSelect byte = {{ .import.runtime.Constants.traceEvGoBlockSelect }} - TraceEvGoBlockSync byte = {{ .import.runtime.Constants.traceEvGoBlockSync }} + TraceEvGoBlockRecv byte = 23 // +checkconst runtime traceEvGoBlockRecv + TraceEvGoBlockSelect byte = 24 // +checkconst runtime traceEvGoBlockSelect + TraceEvGoBlockSync byte = 25 // +checkconst runtime traceEvGoBlockSync ) diff --git a/pkg/sync/runtime_spinning_amd64.s b/pkg/sync/runtime_spinning_amd64.s index 545f9dae6..e15c712af 100644 --- a/pkg/sync/runtime_spinning_amd64.s +++ b/pkg/sync/runtime_spinning_amd64.s @@ -16,7 +16,7 @@ #include "textflag.h" -#define NMSPINNING_OFFSET {{ .import.runtime.schedt.nmspinning.Offset }} +#define NMSPINNING_OFFSET 92 // +checkoffset runtime schedt.nmspinning TEXT ·addrOfSpinning(SB),NOSPLIT,$0-8 LEAQ runtime·sched(SB), AX diff --git a/tools/checkinfo/BUILD b/tools/checkconst/BUILD similarity index 50% rename from tools/checkinfo/BUILD rename to tools/checkconst/BUILD index 5cdc1244a..4269b35a5 100644 --- a/tools/checkinfo/BUILD +++ b/tools/checkconst/BUILD @@ -3,9 +3,13 @@ load("//tools:defs.bzl", "go_library") package(licenses = ["notice"]) go_library( - name = "checkinfo", - srcs = ["checkinfo.go"], + name = "checkconst", + srcs = [ + "checkconst.go", + ], nogo = False, visibility = ["//tools/nogo:__subpackages__"], - deps = ["@org_golang_x_tools//go/analysis:go_default_library"], + deps = [ + "@org_golang_x_tools//go/analysis:go_default_library", + ], ) diff --git a/tools/checkconst/checkconst.go b/tools/checkconst/checkconst.go new file mode 100644 index 000000000..fe526da94 --- /dev/null +++ b/tools/checkconst/checkconst.go @@ -0,0 +1,353 @@ +// Copyright 2021 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package checkconst checks constant values. +// +// This analyzer supports multiple annotations: checkconst, checkoffset, checksize and checkalign. +// Each of these essentially checks the value of the declared constant (or the #define'ed value in +// the case of an assembly file) against the value seen during analysis. If this does not match, +// an error is emitted with the appropriate value for that constant/offset/size/alignment. +package checkconst + +import ( + "bytes" + "fmt" + "go/ast" + "go/format" + "go/token" + "go/types" + "io/ioutil" + "regexp" + "strconv" + "strings" + + "golang.org/x/tools/go/analysis" +) + +var ( + checkconstMagic = "\\+check(const|align|offset|size)" + checkconstRegexp = regexp.MustCompile(checkconstMagic) + constRegexp = regexp.MustCompile("//\\s+" + checkconstMagic + "\\s+([A-Za-z0-9_\\.]+)\\s+([A-Za-z0-9_\\.]+)") + defineRegexp = regexp.MustCompile("#define\\s+[A-Za-z0-9_]+\\s+([A-Za-z0-9_]+\\s*\\+\\s*)*([x0-9]+)\\s+//\\s+" + checkconstMagic + "\\s+([A-Za-z0-9_\\.]+)\\s+([A-Za-z0-9_\\.]+)") +) + +// Analyzer defines the entrypoint. +var Analyzer = &analysis.Analyzer{ + Name: "checkconst", + Doc: "validates basic constants", + Run: run, + FactTypes: []analysis.Fact{ + (*Constants)(nil), + }, +} + +// Constants contains all constant values. +type Constants struct { + Alignments map[string]int64 + Offsets map[string]int64 + Sizes map[string]int64 + Values map[string]string +} + +// AFact implements analysis.Fact.AFact. +func (*Constants) AFact() {} + +// walkObject walks a local object hierarchy. +func (c *Constants) walkObject(pass *analysis.Pass, parents []string, obj types.Object) { + switch x := obj.(type) { + case *types.Const: + name := strings.Join(parents, ".") + c.Values[name] = x.Val().ExactString() + case *types.PkgName: + // Don't walk to other packages. + case *types.Var: + // Add information as a field. + bestEffort(func() { + name := strings.Join(parents, ".") + c.Alignments[name] = pass.TypesSizes.Alignof(x.Type()) + c.Sizes[name] = pass.TypesSizes.Sizeof(x.Type()) + }) + case *types.TypeName: + // Skip if just an alias, or if not underlying type, or if a + // type parameter. If it is not an alias, then it must be + // package-local. + typ := x.Type() + if x.IsAlias() || typ == nil || typ.Underlying() == nil { + break + } + if _, ok := typ.(*types.TypeParam); ok { + break + } + // Add basic information. + bestEffort(func() { + name := strings.Join(parents, ".") + c.Alignments[name] = pass.TypesSizes.Alignof(typ) + c.Sizes[name] = pass.TypesSizes.Sizeof(typ) + }) + // Recurse to fields if this is a definition. + if structType, ok := typ.Underlying().(*types.Struct); ok { + fields := make([]*types.Var, 0, structType.NumFields()) + for i := 0; i < structType.NumFields(); i++ { + fieldObj := structType.Field(i) + fields = append(fields, fieldObj) + c.walkObject(pass, append(parents, fieldObj.Name()), fieldObj) + } + bestEffort(func() { + offsets := pass.TypesSizes.Offsetsof(fields) + for i, field := range fields { + fieldName := strings.Join(append(parents, field.Name()), ".") + c.Offsets[fieldName] = offsets[i] + } + }) + } + } +} + +// bestEffort is a panic/recover wrapper. This is used because the tools +// library occasionally panics due to some type parameter use, and there is +// simple or obvious way to detect these conditions. This should only be used +// when absolutely necessary. +func bestEffort(fn func()) { + defer func() { + recover() + }() + fn() +} + +// walkScope recursively resolves a scope. +func (c *Constants) walkScope(pass *analysis.Pass, parents []string, scope *types.Scope) { + for _, name := range scope.Names() { + c.walkObject(pass, append(parents, name), scope.Lookup(name)) + } +} + +// extractFacts finds all local facts. +func extractFacts(pass *analysis.Pass) { + c := Constants{ + Alignments: make(map[string]int64), + Offsets: make(map[string]int64), + Sizes: make(map[string]int64), + Values: make(map[string]string), + } + + // Accumulate all facts. + c.walkScope(pass, make([]string, 0, 128), pass.Pkg.Scope()) + pass.ExportPackageFact(&c) +} + +// findPackage finds the package by name. +func findPackage(pkg *types.Package, pkgName string) (*types.Package, error) { + if pkgName == "." || pkgName == "" { + return pkg, nil + } + // Attempt to resolve with the full path. + for _, importedPkg := range pkg.Imports() { + if importedPkg.Path() == pkgName { + return importedPkg, nil + } + } + // Attempt to resolve using the short name. + for _, importedPkg := range pkg.Imports() { + if importedPkg.Name() == pkgName { + return importedPkg, nil + } + } + return nil, fmt.Errorf("unable to locate package %q", pkgName) +} + +// matchRegexp performs a regexp match with a sanity check. +func matchRegexp(pass *analysis.Pass, pos func() token.Pos, re *regexp.Regexp, text string) ([]string, bool) { + m := re.FindStringSubmatch(text) + if m == nil && checkconstRegexp.FindString(text) != "" { + pass.Reportf(pos(), "potentially misformed checkconst directives") + } + return m, m != nil +} + +// buildExpected builds the expected value. +func buildExpected(pass *analysis.Pass, pos func() token.Pos, factName, pkgName, objName string) (string, bool) { + // First, resolve the package. + pkg, err := findPackage(pass.Pkg, pkgName) + if err != nil { + pass.Reportf(pos(), "unable to resolve package %q: %v", pkgName, err) + return "", false + } + + // Next, read the appropriate facts. + var ( + c Constants + s string + ok bool + ) + if !pass.ImportPackageFact(pkg, &c) { + pass.Reportf(pos(), "constant package facts for %q are unavailable", pkg.Path()) + return "", false + } + + // Finally, format appropriately. + switch factName { + case "const": + s, ok = c.Values[objName] + case "align": + if v, vOk := c.Alignments[objName]; vOk { + s, ok = fmt.Sprintf("%d", v), true + } + case "offset": + if v, vOk := c.Offsets[objName]; vOk { + s, ok = fmt.Sprintf("%d", v), true + } + case "size": + if v, vOk := c.Sizes[objName]; vOk { + s, ok = fmt.Sprintf("%d", v), true + } + } + if !ok { + pass.Reportf(pos(), "fact of type %s unavailable for %q", factName, objName) + } + return s, ok +} + +// checkAssembly checks assembly annotations. +func checkAssembly(pass *analysis.Pass) error { + for _, filename := range pass.OtherFiles { + if !strings.HasSuffix(filename, ".s") { + continue + } + content, err := ioutil.ReadFile(filename) + if err != nil { + return fmt.Errorf("unable to read assembly file: %w", err) + } + // This uses the technique to report issues for assembly files + // as described by the Go documentation: + // https://pkg.go.dev/golang.org/x/tools/go/analysis#hdr-Pass + tf := pass.Fset.AddFile(filename, -1, len(content)) + tf.SetLinesForContent(content) + lines := strings.Split(string(content), "\n") + for lineNumber, lineContent := range lines { + // N.B. This is not evaluated except lazily, since it + // will generate errors to attempt to grab the position + // at the end of input. Just avoid it. + pos := func() token.Pos { + return tf.LineStart(lineNumber + 1) + } + m, ok := matchRegexp(pass, pos, defineRegexp, lineContent) + if !ok { + continue // Already reported, if needed. + } + newValue, ok := buildExpected(pass, pos, m[3], m[4], m[5]) + if !ok { + continue // Already reported. + } + // Convert our internal string to the given value. This essentially + // canonicalises the literal string provided in the assembly. + v, err := strconv.ParseInt(m[2], 10, 64) + if err == nil && fmt.Sprintf("%v", v) != newValue { + pass.Reportf(pos(), "got value %v, wanted %q", v, newValue) + continue + } else if err != nil && m[2] != newValue { + pass.Reportf(pos(), "got value %q, wanted %q", m[2], newValue) + continue + } + } + } + return nil +} + +// checkConsts walks all package-level const objects. +func checkConsts(pass *analysis.Pass) error { + for _, f := range pass.Files { + for _, decl := range f.Decls { + d, ok := decl.(*ast.GenDecl) + if !ok || d.Tok != token.CONST { + continue + } + findComments := func(vs *ast.ValueSpec) []*ast.Comment { + comments := make([]*ast.Comment, 0) + if d.Doc != nil { + // Include any formally associated doc from the block. + comments = append(comments, d.Doc.List...) + } + if vs.Doc != nil { + // Include any formally associated comments from the value. + comments = append(comments, vs.Doc.List...) + } + for _, cg := range f.Comments { + for _, c := range cg.List { + // Include any comments that appear on the same line + // as the value spec itself, which are not doc comments. + specPosition := pass.Fset.Position(vs.Pos()) + commentPosition := pass.Fset.Position(c.Pos()) + if specPosition.Line == commentPosition.Line && specPosition.Column < commentPosition.Column { + comments = append(comments, c) + } + } + } + return comments + } + for _, spec := range d.Specs { + vs := spec.(*ast.ValueSpec) + var ( + expectedValue string + expectedSet bool + ) + for _, l := range findComments(vs) { + m, ok := matchRegexp(pass, l.Pos, constRegexp, l.Text) + if !ok { + continue // Already reported, if needed. + } + newValue, ok := buildExpected(pass, l.Pos, m[1], m[2], m[3]) + if ok { + if expectedSet && newValue != expectedValue { + pass.Reportf(l.Pos(), "multiple conflicting values") + continue + } + expectedValue = newValue + expectedSet = true + } + } + if !expectedSet { + continue // Nothing was set. + } + // Format the expression. + var buf bytes.Buffer + for _, value := range vs.Values { + if err := format.Node(&buf, pass.Fset, value); err != nil { + pass.Reportf(value.Pos(), "unable to format expression: %v", err) + continue + } + if s := string(buf.Bytes()); s != expectedValue { + pass.Reportf(value.Pos(), "got value %q, wanted %q", s, expectedValue) + continue + } + } + } + } + } + return nil +} + +func run(pass *analysis.Pass) (any, error) { + // Extract all local facts. This is done against the compiled objects, + // rather than the source-level analysis, which is done below. + extractFacts(pass) + + // Check the local package. + if err := checkConsts(pass); err != nil { + return nil, err + } + if err := checkAssembly(pass); err != nil { + return nil, err + } + return nil, nil +} diff --git a/tools/checkinfo/checkinfo.go b/tools/checkinfo/checkinfo.go deleted file mode 100644 index 3871015f1..000000000 --- a/tools/checkinfo/checkinfo.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2021 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package checkinfo attaches basic info to types. -package checkinfo - -import ( - "go/types" - - "golang.org/x/tools/go/analysis" -) - -// Analyzer defines the entrypoint. -var Analyzer = &analysis.Analyzer{ - Name: "checkinfo", - Doc: "annotates types with basic information", - Run: run, - FactTypes: []analysis.Fact{ - (*Align)(nil), - (*Offset)(nil), - (*Size)(nil), - (*Constants)(nil), - }, -} - -// Align is a fact. -type Align int64 - -// AFact implements analysis.Fact.AFact. -func (*Align) AFact() {} - -// Offset is a fact. -type Offset int64 - -// AFact implements analysis.Fact.AFact. -func (*Offset) AFact() {} - -// Size is a fact. -type Size int64 - -// AFact implements analysis.Fact.AFact. -func (*Size) AFact() {} - -// Constants contains all constant values. -type Constants map[string]string - -// AFact implements analysis.Fact.AFact. -func (*Constants) AFact() {} - -// pkg holds package-level facts. -type pkg struct { - constants Constants -} - -func (p *pkg) walkObject(pass *analysis.Pass, obj types.Object) { - switch x := obj.(type) { - case *types.Const: - // Add to the package-global Constants fact. Unexported - // constants can end up as unaddressable via objectpath and - // thus are not accessible in downstream analyzers. - p.constants[x.Name()] = x.Val().ExactString() - case *types.PkgName: - // Don't walk to other packages. - case *types.Var: - // Skip if the var's type is a type parameter. - typ := x.Type() - if _, ok := typ.Underlying().(*types.TypeParam); ok { - break - } - // Add information as a field. - bestEffort(func() { - a := Align(pass.TypesSizes.Alignof(typ)) - s := Size(pass.TypesSizes.Sizeof(typ)) - pass.ExportObjectFact(obj, &a) - pass.ExportObjectFact(obj, &s) - }) - case *types.TypeName: - // Skip if just an alias, or if not underlying type, or if a - // type parameter. If it is not an alias, then it must be - // package-local. - typ := x.Type() - if x.IsAlias() || typ == nil || typ.Underlying() == nil { - break - } - if _, ok := typ.Underlying().(*types.TypeParam); ok { - break - } - // Add basic information. - bestEffort(func() { - a := Align(pass.TypesSizes.Alignof(typ)) - s := Size(pass.TypesSizes.Sizeof(typ)) - pass.ExportObjectFact(obj, &a) - pass.ExportObjectFact(obj, &s) - }) - // Recurse to fields if this is a definition. - if structType, ok := typ.Underlying().(*types.Struct); ok { - fields := make([]*types.Var, 0, structType.NumFields()) - for i := 0; i < structType.NumFields(); i++ { - fieldObj := structType.Field(i) - fields = append(fields, fieldObj) - p.walkObject(pass, fieldObj) - } - bestEffort(func() { - offsets := pass.TypesSizes.Offsetsof(fields) - for i, field := range fields { - pass.ExportObjectFact(field, (*Offset)(&offsets[i])) - } - }) - } - case *types.Func: - // Skip if no underlying type. - if x.Type() == nil { - break - } - // Recurse to all parameters. - sig := x.Type().(*types.Signature) - if recv := sig.Recv(); recv != nil { - p.walkObject(pass, recv) - } - if params := sig.Params(); params != nil { - for i := 0; i < params.Len(); i++ { - p.walkObject(pass, params.At(i)) - } - } - if results := sig.Results(); results != nil { - for i := 0; i < results.Len(); i++ { - p.walkObject(pass, results.At(i)) - } - } - p.walkScope(pass, x.Scope()) - } -} - -// bestEffort is a panic/recover wrapper. This is used because the tools -// package occasionally panics due to some type parameter use, and there is no -// simple or obvious way to detect these conditions. This should only be used -// when absolutely necessary. -func bestEffort(fn func()) { - defer func() { - recover() - }() - fn() -} - -// walkScope recursively resolves a scope. -func (p *pkg) walkScope(pass *analysis.Pass, scope *types.Scope) { - for _, name := range scope.Names() { - p.walkObject(pass, scope.Lookup(name)) - } -} - -func run(pass *analysis.Pass) (any, error) { - p := &pkg{ - constants: make(Constants), - } - - // Export all object facts and accumulate all package facts. - p.walkScope(pass, pass.Pkg.Scope()) - - // Export package facts. - pass.ExportPackageFact(&p.constants) - - return nil, nil -} diff --git a/tools/nogo/check/BUILD b/tools/nogo/check/BUILD index 84d768154..81197db64 100644 --- a/tools/nogo/check/BUILD +++ b/tools/nogo/check/BUILD @@ -14,8 +14,8 @@ go_library( deps = [ "//runsc/flag", "//tools/checkaligned", + "//tools/checkconst", "//tools/checkescape", - "//tools/checkinfo", "//tools/checklinkname", "//tools/checklocks", "//tools/checkunsafe", diff --git a/tools/nogo/check/analyzers.go b/tools/nogo/check/analyzers.go index 5be48f448..7d92dd27c 100644 --- a/tools/nogo/check/analyzers.go +++ b/tools/nogo/check/analyzers.go @@ -18,7 +18,6 @@ import ( "encoding/gob" "io" "reflect" - "strings" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/asmdecl" @@ -50,8 +49,8 @@ import ( "honnef.co/go/tools/stylecheck" "gvisor.dev/gvisor/tools/checkaligned" + "gvisor.dev/gvisor/tools/checkconst" "gvisor.dev/gvisor/tools/checkescape" - "gvisor.dev/gvisor/tools/checkinfo" "gvisor.dev/gvisor/tools/checklinkname" "gvisor.dev/gvisor/tools/checklocks" "gvisor.dev/gvisor/tools/checkunsafe" @@ -94,9 +93,6 @@ var ( // allFactTypes is a list of all fact types, useful as a filter. allFactTypes = make(map[reflect.Type]bool) - - // allFactNames is a list with all fact names. - allFactNames = make(map[reflect.Type]string) ) // findAnalyzer maps orig to an analyzer instance. @@ -107,7 +103,7 @@ func findAnalyzer(orig *analysis.Analyzer) analyzer { return allAnalyzers[orig] } -// registerFactType registers a analysis.Fact. +// registerFactType registers an analysis.Fact. func registerFactType(f analysis.Fact) { // Already registered? t := reflect.TypeOf(f) @@ -118,17 +114,9 @@ func registerFactType(f analysis.Fact) { // Register the type. gob.Register(f) allFactTypes[t] = true - s := t.String() - for len(s) > 0 && s[0] == '*' { - s = s[1:] - } - - // Take only the final element. - parts := strings.Split(s, ".") - allFactNames[t] = parts[len(parts)-1] } -// register recurisvely registers an analyzer. +// register recursively registers an analyzer. func register(a analyzer) { // Already registered? if _, ok := allAnalyzers[a.Legacy()]; ok { @@ -179,7 +167,7 @@ func init() { register(&plainAnalyzer{unsafeptr.Analyzer}) register(&plainAnalyzer{unusedresult.Analyzer}) register(checkescape.Analyzer) - register(&plainAnalyzer{checkinfo.Analyzer}) + register(&plainAnalyzer{checkconst.Analyzer}) register(&plainAnalyzer{checkunsafe.Analyzer}) register(&plainAnalyzer{checklinkname.Analyzer}) register(&plainAnalyzer{checklocks.Analyzer}) diff --git a/tools/nogo/check/check.go b/tools/nogo/check/check.go index 6476fa2e9..b0c908dfe 100644 --- a/tools/nogo/check/check.go +++ b/tools/nogo/check/check.go @@ -655,27 +655,6 @@ func (i *importer) allFactsAndFindings() (FindingSet, *facts.Bundle) { return findings, allFacts } -// Facts runs all analyzers, and returns human-readable facts. -// -// These facts are essentially a dictionary tree (split across all '.' -// characters in the canonical human representation) that can be used for -// rendering via a template. -func Facts(path string, srcs []string) (facts.Resolved, error) { - i := &importer{ - fset: token.NewFileSet(), - cache: make(map[string]*importerEntry), - imports: make(map[string]*types.Package), - } - pkg, _, localFacts, err := i.checkPackage(path, srcs) - if localFacts == nil && err != nil { - // Allow failure here, since we may not care about some - // analyzers for these packages. - return nil, err - } - _, allFacts := i.allFactsAndFindings() - return facts.Resolve(pkg, localFacts, allFacts, allFactNames) -} - // FindRoot finds a package root. func FindRoot(srcs []string, srcRootRegex string) (string, error) { if srcRootRegex == "" { diff --git a/tools/nogo/cli/cli.go b/tools/nogo/cli/cli.go index bc1627df4..0ade483f8 100644 --- a/tools/nogo/cli/cli.go +++ b/tools/nogo/cli/cli.go @@ -23,7 +23,6 @@ import ( "path" "path/filepath" "regexp" - "text/template" "github.com/google/subcommands" "golang.org/x/sys/unix" @@ -421,7 +420,7 @@ func (f *Filter) Execute(ctx context.Context, fs *flag.FlagSet, args ...any) sub } defer closeOutput(output) - // Load and filer available findings. + // Load and filter available findings. var filteredFindings check.FindingSet for _, filename := range fs.Args() { // Note that this applies a caching strategy to the filtered @@ -467,75 +466,12 @@ func (f *Filter) Execute(ctx context.Context, fs *flag.FlagSet, args ...any) sub return subcommands.ExitSuccess } -// Render implements subcommands.Command for the "render" command. -type Render struct { - Template string - Output string -} - -// Name implements subcommands.Command.Name. -func (*Render) Name() string { - return "render" -} - -// Synopsis implements subcommands.Command.Synopsis. -func (*Render) Synopsis() string { - return "Renders facts about a package using a template." -} - -// Usage implements subcommands.Command.Usage. -func (*Render) Usage() string { - return `render - - Loads all data and renders all known facts. Note that render is not - currently compatible with binary analyzers, and these facts will not - be included (unless they come from dependencies). - -` -} - -// SetFlags implements subcommands.Command.SetFlags. -func (r *Render) SetFlags(fs *flag.FlagSet) { - fs.StringVar(&r.Template, "template", "", "text template file for rendering (required)") - fs.StringVar(&r.Output, "output", "", "output file for rendering (or empty for stdout)") -} - -// Execute implements subcommands.Command.Execute. -func (r *Render) Execute(ctx context.Context, fs *flag.FlagSet, args ...any) subcommands.ExitStatus { - // Open the output file. - output, err := openOutput(r.Output, os.Stdout) - if err != nil { - return failure("opening output: %v", err) - } - defer closeOutput(output) - - // Open the template file. - t, err := template.ParseFiles(r.Template) - if err != nil { - return failure("loading template: %v", err) - } - - // Process the facts. - facts, err := check.Facts("main", fs.Args()) - if err != nil { - return failure("%v", err) - } - - // Render as a template. - if err := t.Execute(output, facts); err != nil { - return failure("during render: %v", err) - } - - return subcommands.ExitSuccess -} - // Main is the main entrypoint. func Main() { subcommands.Register(&Check{}, "") subcommands.Register(&Bundle{}, "") subcommands.Register(&Stdlib{}, "") subcommands.Register(&Filter{}, "") - subcommands.Register(&Render{}, "") subcommands.Register(subcommands.HelpCommand(), "") subcommands.Register(subcommands.FlagsCommand(), "") flag.CommandLine.Parse(os.Args[1:]) diff --git a/tools/nogo/defs.bzl b/tools/nogo/defs.bzl index 733d8dc9a..3a9d68f26 100644 --- a/tools/nogo/defs.bzl +++ b/tools/nogo/defs.bzl @@ -450,70 +450,3 @@ def _nogo_aspect_tricorder_impl(target, ctx): nogo_aspect_tricorder = aspect( implementation = _nogo_aspect_tricorder_impl, ) - -def _nogo_facts_impl(ctx): - """Extract nogo facts.""" - - # Build a complete configuration. Note that we don't care about the import - # path, since this will generate facts only. We use ctx as the target here, - # since this will refer to ctx.files (which contains no binaries). - go_ctx, args, inputs, _ = _nogo_package_config(ctx, ctx.attr.deps) - - # Build the runner. - ctx.actions.run( - inputs = inputs + ctx.files.srcs + ctx.files.template, - outputs = [ctx.outputs.output], - tools = depset(go_ctx.runfiles.to_list() + ctx.files._nogo), - executable = ctx.files._nogo[0], - env = go_ctx.env, - mnemonic = "GoStaticAnalysis", - progress_message = "Generating %s" % ctx.label, - # See above. - execution_requirements = {"no-sandbox": "1"}, - arguments = args + [ - "render", - "-template=%s" % ctx.files.template[0].path, - "-output=%s" % ctx.outputs.output.path, - ] + [src.path for src in ctx.files.srcs], - ) - - # Return the output. - return [DefaultInfo(files = depset([ctx.outputs.output]))] - -nogo_facts = go_rule( - rule, - implementation = _nogo_facts_impl, - attrs = { - "srcs": attr.label_list( - allow_files = True, - doc = "Source files to be processed.", - mandatory = True, - ), - "deps": attr.label_list( - aspects = [nogo_aspect], - doc = "Go dependencies to be analyzed.", - ), - "template": attr.label( - allow_files = True, - doc = "Template to be rendered for the output.", - mandatory = True, - ), - "output": attr.output( - doc = "Output file to be rendered.", - mandatory = True, - ), - "_nogo": attr.label( - default = "//tools/nogo:nogo", - cfg = "exec", - ), - # See _nogo_aspect, above. - "_nogo_stdlib": attr.label( - default = "//tools/nogo:stdlib", - cfg = "target", - ), - "_target": attr.label( - default = "//tools/nogo:target", - cfg = "target", - ), - }, -) diff --git a/tools/nogo/facts/facts.go b/tools/nogo/facts/facts.go index 14938d431..642204a56 100644 --- a/tools/nogo/facts/facts.go +++ b/tools/nogo/facts/facts.go @@ -260,144 +260,7 @@ func (b *Bundle) Package(pkg *types.Package) (*Package, error) { } // Nothing available. - return nil, nil -} - -// Resolved is a human-readable fact format. -type Resolved map[string]any - -// addRecursively adds a entry to a map recursively. -// -// Precondition: len(names) > 0. -func (r Resolved) addRecursively(names []string, value any) { - start := r - for i := 0; i < len(names)-1; i++ { - m, ok := start[names[i]] - if !ok { - m = make(Resolved) - start[names[i]] = m - } else { - // This may have been used by a conflicting fact. This - // should be rare, but we ensure that the proper name - // itself is used in the scope instead of the fact. - if _, ok = m.(Resolved); !ok { - m = make(Resolved) - start[names[i]] = m - } - } - start = m.(Resolved) - } - if _, ok := start[names[len(names)-1]]; ok { - // Skip, already exists. See above. - return - } - start[names[len(names)-1]] = value -} - -// addObject adds the object with the given name. -func (r Resolved) addObject(names []string, obj types.Object, facts *Package, allFactNames map[reflect.Type]string) { - for _, fact := range facts.Objects[obj] { - v := reflect.ValueOf(fact) - typeName, ok := allFactNames[v.Type()] - if !ok { - continue - } - for v.Kind() == reflect.Ptr { - v = v.Elem() - } - r.addRecursively(append(names, typeName), v.Interface()) - } -} - -// walkObject resolves all objects recursively. -// -// Parent should be empty or end with a period. -func (r Resolved) walkObject(parents []string, obj types.Object, facts *Package, allFactNames map[reflect.Type]string) { - switch x := obj.(type) { - case *types.TypeName: - s := append(parents, x.Name()) - r.addObject(s, obj, facts, allFactNames) - // Skip if just an alias, or if not underlying type. - if x.IsAlias() || x.Type() == nil || x.Type().Underlying() == nil { - break - } - // Recurse to fields if this is a definition. - if structType, ok := x.Type().Underlying().(*types.Struct); ok { - for i := 0; i < structType.NumFields(); i++ { - r.walkObject(s, structType.Field(i), facts, allFactNames) - } - } - case *types.Func: - // Skip if no underlying type. - if x.Type() == nil { - break - } - // Recurse to all parameters. - sig := x.Type().(*types.Signature) - s := parents - if recv := sig.Recv(); recv != nil { - s = append(s, recv.Type().String()) - } - s = append(s, x.Name()) - r.addObject(s, obj, facts, allFactNames) - if params := sig.Params(); params != nil { - for i := 0; i < params.Len(); i++ { - r.walkObject(s, params.At(i), facts, allFactNames) - } - } - if results := sig.Results(); results != nil { - for i := 0; i < results.Len(); i++ { - r.walkObject(s, results.At(i), facts, allFactNames) - } - } - default: - r.addObject(append(parents, obj.Name()), obj, facts, allFactNames) - } -} - -// walkScope recursively resolves a scope. -func (r Resolved) walkScope(parents []string, scope *types.Scope, facts *Package, allFactNames map[reflect.Type]string) { - for _, name := range scope.Names() { - r.walkObject(parents, scope.Lookup(name), facts, allFactNames) - } -} - -// walkPackage adds all package facts. -func (r Resolved) walkPackage(parents []string, facts *Package, allFactNames map[reflect.Type]string) { - for _, fact := range facts.Objects[nil] { - v := reflect.ValueOf(fact) - typeName, ok := allFactNames[v.Type()] - if !ok { - continue - } - for v.Kind() == reflect.Ptr { - v = v.Elem() - } - r.addRecursively(append(parents, typeName), v.Interface()) - } -} - -// Resolve resolves all object facts. -func Resolve(pkg *types.Package, localFacts *Package, allFacts *Bundle, allFactNames map[reflect.Type]string) (Resolved, error) { - // Populate the tree. Allocating this slice up front prevents - // allocation during name resolution. We allow for up to 64 names - // without allocating a new backing array. - r := make(Resolved) - names := make([]string, 0, 64) - r.walkPackage(names, localFacts, allFactNames) - r.walkScope(names, pkg.Scope(), localFacts, allFactNames) - for _, importPkg := range pkg.Imports() { - importFacts, err := allFacts.Package(importPkg) - if err != nil { - return nil, err - } - if importFacts == nil { - continue // Nothing to render. - } - r.walkPackage(append(names, "import", importPkg.Name()), importFacts, allFactNames) - r.walkScope(append(names, "import", importPkg.Name()), importPkg.Scope(), importFacts, allFactNames) - } - return r, nil + return nil, fmt.Errorf("no facts available for package %q", pkg.Path()) } func init() {