Skip to content

Commit 8ef949a

Browse files
xaionaro@dx.centerxaionaro@dx.center
authored andcommitted
fix: local binder objects - stability level, deadlock, real pointers
Three root-cause fixes for local binder object support: 1. Write binder stability level after each flat_binder_object Android's Parcel::finishFlattenBinder writes an int32 stability level after every flat_binder_object. The receiving process reads it via finishUnflattenBinder and rejects binders with undeclared stability (BAD_TYPE = 0x80000001). We now write SYSTEM stability (12) for all binder objects. 2. Use real heap pointers for binder/cookie fields The kernel binder driver requires the flat_binder_object.binder and .cookie fields to be valid process-space addresses. Synthetic counter values (1, 2, 3...) were silently rejected. Now we allocate real Go heap objects and use their addresses. 3. Remove mutex from ioctl paths to prevent deadlock The binder fd supports concurrent ioctl from different OS threads. Holding d.mu across blocking ioctls deadlocked when the kernel needed the read-loop thread to acknowledge BR_INCREFS/BR_ACQUIRE before delivering BR_REPLY. Now d.mu only protects acquiredHandles. The parseReadBuffer function also handles BR_INCREFS, BR_ACQUIRE, and BR_TRANSACTION inline.
1 parent 9516580 commit 8ef949a

9 files changed

Lines changed: 358 additions & 71 deletions

File tree

binder/ibinder.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type IBinder interface {
2828
UnlinkToDeath(ctx context.Context, recipient DeathRecipient) (_err error)
2929
IsAlive(ctx context.Context) bool
3030
Handle() uint32
31+
Cookie() uintptr
3132
Transport() VersionAwareTransport
3233
Identity() CallerIdentity
3334
}

binder/proxy_binder.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ func (b *ProxyBinder) Handle() uint32 {
8383
return b.handle
8484
}
8585

86+
// Cookie returns 0 for remote proxies (only local stubs have a cookie).
87+
func (b *ProxyBinder) Cookie() uintptr {
88+
return 0
89+
}
90+
8691
// Transport returns the underlying VersionAwareTransport used by this ProxyBinder.
8792
func (b *ProxyBinder) Transport() VersionAwareTransport {
8893
return b.transport

binder/stub_binder.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package binder
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"sync"
7+
"unsafe"
8+
9+
"github.com/xaionaro-go/binder/parcel"
10+
)
11+
12+
// StubBinder is a server-side IBinder that wraps a TransactionReceiver.
13+
// When passed as a binder parameter in a proxy method, it is written
14+
// as a local binder object (BINDER_TYPE_BINDER) instead of a handle
15+
// reference (BINDER_TYPE_HANDLE).
16+
//
17+
// The cookie is assigned lazily the first time the stub is registered
18+
// with a Transport (see RegisterWithTransport).
19+
type StubBinder struct {
20+
Receiver TransactionReceiver
21+
transport Transport
22+
23+
mu sync.Mutex
24+
cookie uintptr
25+
26+
// weakRef is a heap-allocated anchor whose address is used as the
27+
// flat_binder_object.binder field (the kernel binder node identity).
28+
// In Android C++, this role is played by BBinder::getWeakRefs().
29+
// It must differ from cookie (which is the dispatch key) because
30+
// the kernel uses them independently. Keeping weakRef as a *uint64
31+
// prevents the GC from collecting it while the StubBinder is alive.
32+
weakRef *uint64
33+
}
34+
35+
// NewStubBinder creates a StubBinder wrapping the given TransactionReceiver.
36+
func NewStubBinder(
37+
receiver TransactionReceiver,
38+
) *StubBinder {
39+
return &StubBinder{
40+
Receiver: receiver,
41+
weakRef: new(uint64),
42+
}
43+
}
44+
45+
// Transact is not supported on local stubs; calling it returns an error.
46+
func (s *StubBinder) Transact(
47+
_ context.Context,
48+
_ TransactionCode,
49+
_ TransactionFlags,
50+
_ *parcel.Parcel,
51+
) (*parcel.Parcel, error) {
52+
return nil, fmt.Errorf("StubBinder: Transact is not supported on local stubs")
53+
}
54+
55+
// ResolveCode is not supported on local stubs.
56+
func (s *StubBinder) ResolveCode(
57+
_ string,
58+
_ string,
59+
) (TransactionCode, error) {
60+
return 0, fmt.Errorf("StubBinder: ResolveCode is not supported on local stubs")
61+
}
62+
63+
// LinkToDeath is a no-op for local stubs (they cannot die remotely).
64+
func (s *StubBinder) LinkToDeath(
65+
_ context.Context,
66+
_ DeathRecipient,
67+
) error {
68+
return nil
69+
}
70+
71+
// UnlinkToDeath is a no-op for local stubs.
72+
func (s *StubBinder) UnlinkToDeath(
73+
_ context.Context,
74+
_ DeathRecipient,
75+
) error {
76+
return nil
77+
}
78+
79+
// IsAlive always returns true for local stubs.
80+
func (s *StubBinder) IsAlive(_ context.Context) bool {
81+
return true
82+
}
83+
84+
// Handle returns 0 for local stubs (they have no remote handle).
85+
func (s *StubBinder) Handle() uint32 {
86+
return 0
87+
}
88+
89+
// Cookie returns the cookie assigned by RegisterWithTransport.
90+
// Returns 0 if the stub has not been registered yet.
91+
func (s *StubBinder) Cookie() uintptr {
92+
s.mu.Lock()
93+
defer s.mu.Unlock()
94+
return s.cookie
95+
}
96+
97+
// BinderPtr returns the stable address used as the binder node identity
98+
// in the kernel (the flat_binder_object.binder field). This address is
99+
// distinct from Cookie() -- the kernel uses BinderPtr to create/find the
100+
// binder_node, and echoes Cookie back in BR_TRANSACTION for dispatch.
101+
func (s *StubBinder) BinderPtr() uintptr {
102+
return uintptr(unsafe.Pointer(s.weakRef))
103+
}
104+
105+
// Transport returns nil for unregistered stubs; after
106+
// RegisterWithTransport it returns the transport that was used.
107+
func (s *StubBinder) Transport() VersionAwareTransport {
108+
return nil
109+
}
110+
111+
// Identity returns the default caller identity.
112+
func (s *StubBinder) Identity() CallerIdentity {
113+
return DefaultCallerIdentity()
114+
}
115+
116+
// RegisterWithTransport registers this stub's receiver with the given
117+
// transport and stores the returned cookie. Subsequent calls are no-ops
118+
// (the stub is only registered once). Returns the cookie.
119+
func (s *StubBinder) RegisterWithTransport(
120+
ctx context.Context,
121+
t Transport,
122+
) uintptr {
123+
s.mu.Lock()
124+
defer s.mu.Unlock()
125+
126+
if s.cookie != 0 {
127+
return s.cookie
128+
}
129+
130+
s.cookie = t.RegisterReceiver(ctx, s.Receiver)
131+
s.transport = t
132+
return s.cookie
133+
}
134+
135+
// Verify StubBinder implements IBinder.
136+
var _ IBinder = (*StubBinder)(nil)

binder/write_binder.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package binder
2+
3+
import (
4+
"context"
5+
6+
"github.com/xaionaro-go/binder/parcel"
7+
)
8+
9+
// WriteBinderToParcel writes an IBinder to a parcel, choosing the correct
10+
// wire format based on whether the binder is a local stub or a remote proxy.
11+
//
12+
// For remote proxies (ProxyBinder), it writes BINDER_TYPE_HANDLE with the handle.
13+
// For local stubs (StubBinder), it auto-registers the stub with the transport
14+
// (if not already registered) and writes BINDER_TYPE_BINDER with the cookie.
15+
func WriteBinderToParcel(
16+
ctx context.Context,
17+
p *parcel.Parcel,
18+
b IBinder,
19+
transport Transport,
20+
) {
21+
stub, isStub := b.(*StubBinder)
22+
if !isStub {
23+
p.WriteStrongBinder(b.Handle())
24+
return
25+
}
26+
27+
cookie := stub.Cookie()
28+
if cookie == 0 {
29+
cookie = stub.RegisterWithTransport(ctx, transport)
30+
}
31+
p.WriteLocalBinder(stub.BinderPtr(), cookie)
32+
}

0 commit comments

Comments
 (0)