Repository navigation
Conversation
- cpu: add amd64 v2/v3/v4 feature bits
- darwin: expose setpriority PRIO_DARWIN_* constants
- unix/darwin: add missing pwritev test case on darwin/arm64
- unix/darwin: add O_EXEC and O_SEARCH on darwin
- unix/darwin: pass fcntl data as unsafe.Pointer in FcntlF{lock,store} on darwin
- unix/linux: don't warn about closed stderr from child process
- unix/linux: unify ppc64x syscall wrappers
- unix: add listns syscall introduced in Linux 6.19
- unix: add Mmsghdr for linux
- unix: add PrctlArg5Ptr
- unix: align the getdents buffer
- unix: extend z/OS support
- unix: keep generated types compilable with current cgo
- unix: pass xattr data as unsafe.Pointer in BSD xattr syscalls
- unix: provide linux/sparc64 assembly for syscalls
- unix: return syscall error in *listxattrNS on BSD
- unix: update glibc to 2.44
- unix: update linux to v7.2
- unix: use the stat64 family and fix Stat_t on linux/sparc64
- windows: drop arm
full diff: golang/sys@v0.48.0...v0.49.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
full diff: golang/mod@v0.41.0...v0.42.0 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
full diff: golang/sync@v0.23.0...v0.24.0 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
full diff: golang/term@v0.46.0...v0.47.0 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
- secure/bidirule: lazily precompute ASCII table - unicode/bidi: avoid quadratic behavior in Paragraph.Order and Paragraph.Line full diff: golang/text@v0.42.0...v0.43.0 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
- quic: properly check for allowed frame types in a packet full diff: golang/net@v0.60.0...v0.61.0 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🔴 CRITICAL
This vendor update introduces new hand-written z/OS ptrace support code (ptrace_zos.go and ptrace_zos_singlestep.go) with several correctness bugs. The generated syscall wrappers and constant definitions for other platforms appear correct. The golang.org/x/text bidi algorithm changes are clean fixes with no introduced bugs.
The critical issues are all in the new z/OS ptrace implementation and stem from two root causes:
- A logic ordering error that permanently disables the PSW correction in single-step emulation
- A systematic misuse of
unsafe.Pointer(uintptr(integer))that the code's own comment explicitly warns against
| // 9. Back up PSW by 2 bytes (to point at original instruction, not SVC 144) | ||
| // Use direct PSW write like ztrace does, not full register set | ||
| hitAddr := regs.Psw.Addr | ||
| if state.isTempBreakpoint(hitAddr - 2) { |
There was a problem hiding this comment.
[high] PSW adjustment never applied: removeTempBreakpoints clears active flags before isTempBreakpoint is called
In PtraceSingleStep, step 8 (line 402) calls state.removeTempBreakpoints(pid), which sets every breakpoint's active field to false. Step 9 (line 409) then calls state.isTempBreakpoint(hitAddr - 2), which checks s.breakpoints[i].active. Because active is already false for all breakpoints, isTempBreakpoint unconditionally returns false and the PSW backup by 2 bytes is never executed.
The effect: after a single-step, the program counter points at the SVC 144 instruction (the breakpoint trap) rather than the original instruction. Single-step emulation on z/OS is silently broken by this PR.
The fix is to check the breakpoint address before (or during) removal — for example, capture the set of active breakpoint addresses before calling removeTempBreakpoints, then compare against that saved set:
// Before removing breakpoints, save their addresses
hitBreakpointAddr := uint64(0)
for _, bp := range state.breakpoints {
if bp.active && bp.address == hitAddr-2 {
hitBreakpointAddr = bp.address
break
}
}
if err := state.removeTempBreakpoints(pid); err != nil {
return err
}
if hitBreakpointAddr != 0 {
if err := ptraceWritePSW(pid, uint32(hitBreakpointAddr)); err != nil {
return err
}
}| Confidence | Score |
|---|---|
| 🟢 strong | 100/100 |
| // z/OS PT_READ_BLOCK can read blocks directly without word-by-word alignment | ||
| // The buffer parameter points to the output buffer | ||
| buffer := unsafe.Pointer(&out[0]) | ||
| err = ptracePtrWithBuffer(req, pid, addr, unsafe.Pointer(uintptr(len(out))), buffer) |
There was a problem hiding this comment.
[high] unsafe.Pointer(uintptr(len(out))) passes an integer as unsafe.Pointer — the pattern the code's own comment explicitly forbids
The ptrace() function at line 55 contains a prominent comment explaining exactly why passing an integer as unsafe.Pointer to Bpx4ptr is dangerous:
We must not pass it to
Bpx4ptrasunsafe.Pointer(data)because the garbage collector would treat that integer as a pointer and may crash trying to follow it. Instead we inline the parms array that Bpx4ptr would construct, placing&dataatparms[3].
ptracePeek (and ptracePoke at line 236) violates this rule by calling ptracePtrWithBuffer with unsafe.Pointer(uintptr(len(out))). The len(out) value is a byte count integer, not a Go-managed pointer. When Bpx4ptr stores it as unsafe.Pointer(data) in its local parms array and passes parms[3] to bpxcall, BPX4PTR dereferences parms[3] to obtain the data value — meaning it will interpret the integer as a target-process address, not as the intended byte count.
The correct fix follows the same pattern as ptrace(): store the count in a local variable and pass its address:
| err = ptracePtrWithBuffer(req, pid, addr, unsafe.Pointer(uintptr(len(out))), buffer) | |
| countVal := uintptr(len(out)) | |
| err = ptracePtrWithBuffer(req, pid, addr, unsafe.Pointer(&countVal), buffer) |
| Confidence | Score |
|---|---|
| 🟡 moderate | 57/100 |
| func ptraceWritePSW(pid int, pswAddr uint32) error { | ||
| const PTRACE_REG_PSWA = 41 | ||
| rv, rc, rn := Bpx4ptr(int32(PT_WRITE_GPR), int32(pid), | ||
| unsafe.Pointer(uintptr(PTRACE_REG_PSWA)), |
There was a problem hiding this comment.
[high] ptraceWritePSW passes integer constants directly as unsafe.Pointer to Bpx4ptr
ptraceWritePSW calls Bpx4ptr directly with:
unsafe.Pointer(uintptr(PTRACE_REG_PSWA))— the integer constant 41 cast to a pointerunsafe.Pointer(uintptr(pswAddr))— auint32register address value cast to a pointer
The code comment in ptrace() explains why this is wrong: BPX4PTR receives the parms array where parms[2] = &addr and parms[3] = &data, and dereferences them to obtain the actual values. When Bpx4ptr is called with a raw integer as addr/data, BPX4PTR will interpret those integers as addresses into the target process — not as the register index and PSW address value intended.
The correct fix stores each integer in a local variable and passes its address, matching the pattern in ptrace() and ptracePtr():
| unsafe.Pointer(uintptr(PTRACE_REG_PSWA)), | |
| addrVal := uintptr(PTRACE_REG_PSWA) | |
| dataVal := uintptr(pswAddr) | |
| rv, rc, rn := Bpx4ptr(int32(PT_WRITE_GPR), int32(pid), | |
| unsafe.Pointer(&addrVal), | |
| unsafe.Pointer(&dataVal), | |
| nil) |
| Confidence | Score |
|---|---|
| 🟡 moderate | 57/100 |
|
|
||
| // Execute block request | ||
| rvBlk, rcBlk, rnBlk := Bpx4ptr(int32(PT_BLOCKREQ), int32(pid), | ||
| unsafe.Pointer(&buf[0]), unsafe.Pointer(uintptr(totalSize)), nil) |
There was a problem hiding this comment.
[high] PtraceGetRegs (and PtraceSetRegs) pass unsafe.Pointer(uintptr(totalSize)) to Bpx4ptr — integer-as-pointer pattern
Both PtraceGetRegs (line 334) and PtraceSetRegs (line 484) call Bpx4ptr with unsafe.Pointer(uintptr(totalSize)) as the data parameter. totalSize is a plain int byte count, not a pointer.
As the code comment in ptrace() explains, Bpx4ptr stores data in its parms array as unsafe.Pointer(&data). BPX4PTR then dereferences parms[3] to recover the original value. When the integer totalSize is passed directly as unsafe.Pointer, BPX4PTR receives and dereferences the integer as if it were an address — not as the intended block size. This means PT_BLOCKREQ is given a garbled size value.
Fix: store totalSize in a uintptr local and pass its address:
| unsafe.Pointer(&buf[0]), unsafe.Pointer(uintptr(totalSize)), nil) | |
| totalSizeVal := uintptr(totalSize) | |
| rvBlk, rcBlk, rnBlk := Bpx4ptr(int32(PT_BLOCKREQ), int32(pid), | |
| unsafe.Pointer(&buf[0]), unsafe.Pointer(&totalSizeVal), nil) |
(Apply the same fix to the identical call in PtraceSetRegs at line 484.)
| Confidence | Score |
|---|---|
| 🟡 moderate | 57/100 |
| } | ||
|
|
||
| // ptracePoke implements the poke operations using PT_WRITE_BLOCK. | ||
| func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (count int, err error) { |
There was a problem hiding this comment.
[medium] ptracePoke accepts a peekReq parameter but never uses it
The function signature ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) accepts a peekReq argument, and callers pass PT_READ_BLOCK for it. However, the function body never references peekReq — it only calls ptracePtrWithBuffer(pokeReq, ...).
On Linux, ptrace write operations on unaligned/partial-word boundaries require a read-modify-write cycle (peek the current word, merge the new bytes, poke the result). The peekReq parameter name strongly implies this same logic was intended here. If z/OS PT_WRITE_BLOCK handles alignment internally, the parameter is simply dead code and should be removed (or documented as intentionally unused with a comment). If alignment handling is required, the implementation is incomplete.
Consider either removing the unused parameter or adding a comment explaining why it is not needed on z/OS:
| func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (count int, err error) { | |
| func ptracePoke(pokeReq int, _ int, pid int, addr uintptr, data []byte) (count int, err error) { |
| Confidence | Score |
|---|---|
| 🟡 moderate | 75/100 |
vendor: golang.org/x/sys v0.49.0
full diff: golang/sys@v0.48.0...v0.49.0
vendor: golang.org/x/mod v0.42.0
full diff: golang/mod@v0.41.0...v0.42.0
vendor: golang.org/x/sync v0.24.0
full diff: golang/sync@v0.23.0...v0.24.0
vendor: golang.org/x/term v0.47.0
full diff: golang/term@v0.46.0...v0.47.0
vendor: golang.org/x/text v0.43.0
full diff: golang/text@v0.42.0...v0.43.0
vendor: golang.org/x/net v0.61.0
full diff: golang/net@v0.60.0...v0.61.0
Release notes (optional)
A picture of a cute animal (not mandatory but encouraged)