fix(platform): correct darwin process ABI and argv bounds
- commit
- 898d27698c07d3e4b690d7d879e3125318901724
- author
- brice · · 2026-07-16 18:17 UTC
- parent
- fe5ad84
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
4 files · +37 -2
M internal/platform/process.go +6 -1
| @@ -36,7 +36,7 @@ func parseKernProcArgs2(data []byte) ([]string, error) { | |||
| 36 | 36 | if len(data) < argcSize { | |
| 37 | 37 | return nil, fmt.Errorf("KERN_PROCARGS2: truncated argc") | |
| 38 | 38 | } | |
| 39 | - | argc := int(binary.LittleEndian.Uint32(data[:argcSize])) | |
| 39 | + | argc := int(int32(binary.LittleEndian.Uint32(data[:argcSize]))) | |
| 40 | 40 | if argc <= 0 { | |
| 41 | 41 | return nil, fmt.Errorf("KERN_PROCARGS2: invalid argc %d", argc) | |
| 42 | 42 | } | |
| @@ -50,6 +50,11 @@ func parseKernProcArgs2(data []byte) ([]string, error) { | |||
| 50 | 50 | for len(rest) > 0 && rest[0] == 0 { | |
| 51 | 51 | rest = rest[1:] | |
| 52 | 52 | } | |
| 53 | + | // Each argument must contain at least its terminating NUL byte. Check this | |
| 54 | + | // before allocating from the kernel-provided argc. | |
| 55 | + | if argc > len(rest) { | |
| 56 | + | return nil, fmt.Errorf("KERN_PROCARGS2: argc %d exceeds remaining data", argc) | |
| 57 | + | } | |
| 53 | 58 | ||
| 54 | 59 | argv := make([]string, 0, argc) | |
| 55 | 60 | for len(argv) < argc { | |
| @@ -36,7 +36,7 @@ func parseKernProcArgs2(data []byte) ([]string, error) { | |||||
| 36 | if len(data) < argcSize { | 36 | if len(data) < argcSize { | ||
| 37 | return nil, fmt.Errorf("KERN_PROCARGS2: truncated argc") | 37 | return nil, fmt.Errorf("KERN_PROCARGS2: truncated argc") | ||
| 38 | } | 38 | } | ||
| 39 | - | argc := int(binary.LittleEndian.Uint32(data[:argcSize])) | 39 | + | argc := int(int32(binary.LittleEndian.Uint32(data[:argcSize]))) |
| 40 | if argc <= 0 { | 40 | if argc <= 0 { | ||
| 41 | return nil, fmt.Errorf("KERN_PROCARGS2: invalid argc %d", argc) | 41 | return nil, fmt.Errorf("KERN_PROCARGS2: invalid argc %d", argc) | ||
| 42 | } | 42 | } | ||
| @@ -50,6 +50,11 @@ func parseKernProcArgs2(data []byte) ([]string, error) { | |||||
| 50 | for len(rest) > 0 && rest[0] == 0 { | 50 | for len(rest) > 0 && rest[0] == 0 { | ||
| 51 | rest = rest[1:] | 51 | rest = rest[1:] | ||
| 52 | } | 52 | } | ||
| 53 | + | // Each argument must contain at least its terminating NUL byte. Check this | |||
| 54 | + | // before allocating from the kernel-provided argc. | |||
| 55 | + | if argc > len(rest) { | |||
| 56 | + | return nil, fmt.Errorf("KERN_PROCARGS2: argc %d exceeds remaining data", argc) | |||
| 57 | + | } | |||
| 53 | 58 | ||||
| 54 | argv := make([]string, 0, argc) | 59 | argv := make([]string, 0, argc) | ||
| 55 | for len(argv) < argc { | 60 | for len(argv) < argc { | ||
M internal/platform/process_darwin_arm64.go +0 -1
| @@ -238,7 +238,6 @@ type externProc struct { | |||
| 238 | 238 | Un [16]byte | |
| 239 | 239 | Vmspace uintptr | |
| 240 | 240 | Sigacts uintptr | |
| 241 | - | _ [3]byte | |
| 242 | 241 | Flag int32 | |
| 243 | 242 | Stat int8 | |
| 244 | 243 | Pid int32 | |
| @@ -238,7 +238,6 @@ type externProc struct { | |||||
| 238 | Un [16]byte | 238 | Un [16]byte | ||
| 239 | Vmspace uintptr | 239 | Vmspace uintptr | ||
| 240 | Sigacts uintptr | 240 | Sigacts uintptr | ||
| 241 | - | _ [3]byte | |||
| 242 | Flag int32 | 241 | Flag int32 | ||
| 243 | Stat int8 | 242 | Stat int8 | ||
| 244 | Pid int32 | 243 | Pid int32 | ||
M internal/platform/process_darwin_arm64_test.go +21 -0
| @@ -6,8 +6,29 @@ import ( | |||
| 6 | 6 | "os" | |
| 7 | 7 | "syscall" | |
| 8 | 8 | "testing" | |
| 9 | + | "unsafe" | |
| 9 | 10 | ) | |
| 10 | 11 | ||
| 12 | + | func TestDarwinKinfoProcLayout(t *testing.T) { | |
| 13 | + | tests := []struct { | |
| 14 | + | name string | |
| 15 | + | got uintptr | |
| 16 | + | want uintptr | |
| 17 | + | }{ | |
| 18 | + | {"Proc.Pid", unsafe.Offsetof(kinfoProc{}.Proc) + unsafe.Offsetof(externProc{}.Pid), 40}, | |
| 19 | + | {"Proc.Comm", unsafe.Offsetof(kinfoProc{}.Proc) + unsafe.Offsetof(externProc{}.Comm), 243}, | |
| 20 | + | {"Eproc.Pgid", unsafe.Offsetof(kinfoProc{}.Eproc) + unsafe.Offsetof(eproc{}.Pgid), 564}, | |
| 21 | + | {"kinfoProc size", unsafe.Sizeof(kinfoProc{}), darwinKinfoProcSize}, | |
| 22 | + | } | |
| 23 | + | for _, tt := range tests { | |
| 24 | + | t.Run(tt.name, func(t *testing.T) { | |
| 25 | + | if tt.got != tt.want { | |
| 26 | + | t.Fatalf("%s = %d; want %d", tt.name, tt.got, tt.want) | |
| 27 | + | } | |
| 28 | + | }) | |
| 29 | + | } | |
| 30 | + | } | |
| 31 | + | ||
| 11 | 32 | func TestDarwinProcessInspectorCurrentProcess(t *testing.T) { | |
| 12 | 33 | inspector := NewProcessInspector() | |
| 13 | 34 | pid := os.Getpid() | |
| @@ -6,8 +6,29 @@ import ( | |||||
| 6 | "os" | 6 | "os" | ||
| 7 | "syscall" | 7 | "syscall" | ||
| 8 | "testing" | 8 | "testing" | ||
| 9 | + | "unsafe" | |||
| 9 | ) | 10 | ) | ||
| 10 | 11 | ||||
| 12 | + | func TestDarwinKinfoProcLayout(t *testing.T) { | |||
| 13 | + | tests := []struct { | |||
| 14 | + | name string | |||
| 15 | + | got uintptr | |||
| 16 | + | want uintptr | |||
| 17 | + | }{ | |||
| 18 | + | {"Proc.Pid", unsafe.Offsetof(kinfoProc{}.Proc) + unsafe.Offsetof(externProc{}.Pid), 40}, | |||
| 19 | + | {"Proc.Comm", unsafe.Offsetof(kinfoProc{}.Proc) + unsafe.Offsetof(externProc{}.Comm), 243}, | |||
| 20 | + | {"Eproc.Pgid", unsafe.Offsetof(kinfoProc{}.Eproc) + unsafe.Offsetof(eproc{}.Pgid), 564}, | |||
| 21 | + | {"kinfoProc size", unsafe.Sizeof(kinfoProc{}), darwinKinfoProcSize}, | |||
| 22 | + | } | |||
| 23 | + | for _, tt := range tests { | |||
| 24 | + | t.Run(tt.name, func(t *testing.T) { | |||
| 25 | + | if tt.got != tt.want { | |||
| 26 | + | t.Fatalf("%s = %d; want %d", tt.name, tt.got, tt.want) | |||
| 27 | + | } | |||
| 28 | + | }) | |||
| 29 | + | } | |||
| 30 | + | } | |||
| 31 | + | ||||
| 11 | func TestDarwinProcessInspectorCurrentProcess(t *testing.T) { | 32 | func TestDarwinProcessInspectorCurrentProcess(t *testing.T) { | ||
| 12 | inspector := NewProcessInspector() | 33 | inspector := NewProcessInspector() | ||
| 13 | pid := os.Getpid() | 34 | pid := os.Getpid() | ||
M internal/platform/process_linux_test.go +10 -0
| @@ -174,6 +174,16 @@ func TestParseKernProcArgs2(t *testing.T) { | |||
| 174 | 174 | data: append([]byte{1, 0, 0, 0}, []byte("/bin/tool\x00tool")...), | |
| 175 | 175 | wantErr: true, | |
| 176 | 176 | }, | |
| 177 | + | { | |
| 178 | + | name: "negative argc", | |
| 179 | + | data: append([]byte{0xff, 0xff, 0xff, 0xff}, []byte("/bin/tool\x00tool\x00")...), | |
| 180 | + | wantErr: true, | |
| 181 | + | }, | |
| 182 | + | { | |
| 183 | + | name: "argc exceeds remaining data", | |
| 184 | + | data: append([]byte{0xff, 0xff, 0xff, 0x7f}, []byte("/bin/tool\x00tool\x00")...), | |
| 185 | + | wantErr: true, | |
| 186 | + | }, | |
| 177 | 187 | } | |
| 178 | 188 | ||
| 179 | 189 | for _, tt := range tests { | |
| @@ -174,6 +174,16 @@ func TestParseKernProcArgs2(t *testing.T) { | |||||
| 174 | data: append([]byte{1, 0, 0, 0}, []byte("/bin/tool\x00tool")...), | 174 | data: append([]byte{1, 0, 0, 0}, []byte("/bin/tool\x00tool")...), | ||
| 175 | wantErr: true, | 175 | wantErr: true, | ||
| 176 | }, | 176 | }, | ||
| 177 | + | { | |||
| 178 | + | name: "negative argc", | |||
| 179 | + | data: append([]byte{0xff, 0xff, 0xff, 0xff}, []byte("/bin/tool\x00tool\x00")...), | |||
| 180 | + | wantErr: true, | |||
| 181 | + | }, | |||
| 182 | + | { | |||
| 183 | + | name: "argc exceeds remaining data", | |||
| 184 | + | data: append([]byte{0xff, 0xff, 0xff, 0x7f}, []byte("/bin/tool\x00tool\x00")...), | |||
| 185 | + | wantErr: true, | |||
| 186 | + | }, | |||
| 177 | } | 187 | } | ||
| 178 | 188 | ||||
| 179 | for _, tt := range tests { | 189 | for _, tt := range tests { | ||