Skip to content

Commit 57b99e9

Browse files
xaionaro@dx.centerxaionaro@dx.center
authored andcommitted
Fix split BR_TRANSACTION_COMPLETE/BR_REPLY and EINTR handling
Two binder protocol bugs fixed: 1. The kernel can send BR_TRANSACTION_COMPLETE and BR_REPLY in separate ioctl reads. When the target service hasn't responded by the time the first ioctl returns, we only get TC. Previously we returned an empty parcel; now we issue another read ioctl to wait for BR_REPLY. This fixes ~17% failure rate under rapid open/close cycles (50 iterations: 250/250 pass, was ~40/50). 2. EINTR (interrupted system call) on any ioctl is now retried instead of returning an error.
1 parent 629c19f commit 57b99e9

2 files changed

Lines changed: 75 additions & 25 deletions

File tree

kernelbinder/driver.go

Lines changed: 75 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -222,23 +222,67 @@ func (d *Driver) Transact(
222222
readBuffer: uint64(uintptr(unsafe.Pointer(&readBuf[0]))),
223223
}
224224

225-
_, _, errno := unix.Syscall(
226-
unix.SYS_IOCTL,
227-
uintptr(d.fd),
228-
binderWriteReadIoctl,
229-
uintptr(unsafe.Pointer(&bwr)),
230-
)
231-
if errno != 0 {
232-
return nil, &aidlerrors.BinderError{Op: "ioctl(BINDER_WRITE_READ)", Err: errno}
225+
for {
226+
_, _, errno := unix.Syscall(
227+
unix.SYS_IOCTL,
228+
uintptr(d.fd),
229+
binderWriteReadIoctl,
230+
uintptr(unsafe.Pointer(&bwr)),
231+
)
232+
switch errno {
233+
case 0:
234+
// Success — proceed to parse.
235+
case unix.EINTR:
236+
// Retry on interrupted system call.
237+
// Reset write buffer (already consumed) but keep read buffer.
238+
bwr.writeSize = 0
239+
continue
240+
default:
241+
return nil, &aidlerrors.BinderError{Op: "ioctl(BINDER_WRITE_READ)", Err: errno}
242+
}
243+
break
233244
}
234245

235-
// Parse the read buffer for BR codes.
236-
reply, err := d.parseReadBuffer(ctx, readBuf[:bwr.readConsumed])
237-
if err != nil {
238-
return nil, err
239-
}
246+
// Parse the read buffer for BR codes. The kernel may split
247+
// BR_TRANSACTION_COMPLETE and BR_REPLY across separate ioctl reads —
248+
// if we got TC but no reply yet, read again to wait for BR_REPLY.
249+
isOneway := flags&binder.FlagOneway != 0
250+
for {
251+
reply, err := d.parseReadBuffer(ctx, readBuf[:bwr.readConsumed])
252+
if err != nil {
253+
return nil, err
254+
}
240255

241-
return reply, nil
256+
// If we got a non-empty reply, or this is a oneway transaction
257+
// (which never gets BR_REPLY), return immediately.
258+
if reply.Len() > 0 || isOneway {
259+
return reply, nil
260+
}
261+
262+
// Empty reply on a non-oneway transaction means the kernel sent
263+
// BR_TRANSACTION_COMPLETE but BR_REPLY hasn't arrived yet.
264+
// Issue a read-only ioctl to wait for it.
265+
logger.Debugf(ctx, "Transact: got BR_TRANSACTION_COMPLETE without BR_REPLY, reading again")
266+
bwr.writeSize = 0
267+
bwr.writeConsumed = 0
268+
bwr.readConsumed = 0
269+
for {
270+
_, _, errno := unix.Syscall(
271+
unix.SYS_IOCTL,
272+
uintptr(d.fd),
273+
binderWriteReadIoctl,
274+
uintptr(unsafe.Pointer(&bwr)),
275+
)
276+
switch errno {
277+
case 0:
278+
case unix.EINTR:
279+
continue
280+
default:
281+
return nil, &aidlerrors.BinderError{Op: "ioctl(BINDER_WRITE_READ/read)", Err: errno}
282+
}
283+
break
284+
}
285+
}
242286
}
243287

244288
// parseReadBuffer processes BR_* codes from the read buffer and returns the reply parcel.
@@ -496,17 +540,24 @@ func (d *Driver) writeCommand(
496540
writeBuffer: uint64(uintptr(unsafe.Pointer(&writeBuf[0]))),
497541
}
498542

499-
_, _, errno := unix.Syscall(
500-
unix.SYS_IOCTL,
501-
uintptr(d.fd),
502-
binderWriteReadIoctl,
503-
uintptr(unsafe.Pointer(&bwr)),
504-
)
505-
if errno != 0 {
506-
return &aidlerrors.BinderError{Op: "ioctl(BINDER_WRITE_READ)", Err: errno}
543+
for {
544+
_, _, errno := unix.Syscall(
545+
unix.SYS_IOCTL,
546+
uintptr(d.fd),
547+
binderWriteReadIoctl,
548+
uintptr(unsafe.Pointer(&bwr)),
549+
)
550+
switch errno {
551+
case 0:
552+
return nil
553+
case unix.EINTR:
554+
// Retry on interrupted system call — the command was not
555+
// processed and must be resent.
556+
continue
557+
default:
558+
return &aidlerrors.BinderError{Op: "ioctl(BINDER_WRITE_READ)", Err: errno}
559+
}
507560
}
508-
509-
return nil
510561
}
511562

512563
// copyStructToBytes copies a struct's raw memory into a byte slice.

tests/e2e/aidlcli_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,6 @@ func logAndExit(msg string) {
191191
// Arguments are joined into a single shell command string to preserve
192192
// empty/quoted values across the adb shell boundary.
193193
func runAidlcli(args ...string) (string, string, error) {
194-
// Build a single shell command string to avoid adb shell arg splitting issues.
195194
parts := make([]string, 0, len(args)+3)
196195
parts = append(parts, deviceBinary, "--format", "json")
197196
for _, a := range args {

0 commit comments

Comments
 (0)