ohmu
·
2026-10-15
1#include "common/android/binder.h"
2#include "common/debug.h"
3#include "common/io.h"
4
5#include <errno.h>
6#include <fcntl.h>
7#include <sys/mman.h>
8#include <unistd.h>
9
10const char* ffBinderOpen(FFBinder* binder) {
11 *binder = (FFBinder) { .fd = -1, .shared = nullptr, .sharedSize = 0, .protocolVersion = 0 };
12
13 FF_AUTO_CLOSE_FD int fd = open(FF_BINDER_DEVICE, O_RDWR | O_CLOEXEC);
14 if (fd < 0) {
15 FF_DEBUG("open(" FF_BINDER_DEVICE ") failed: %s", strerror(errno));
16 return "Failed to open " FF_BINDER_DEVICE;
17 }
18
19 struct binder_version version = { .protocol_version = 0 };
20 const int versionIoctl = ioctl(fd, BINDER_VERSION, &version);
21 if (versionIoctl != 0 || version.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION) {
22 FF_DEBUG("BINDER_VERSION reported protocol %u, expected %u (%s)",
23 version.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION,
24 versionIoctl != 0 ? strerror(errno) : "version mismatch");
25 return "Unsupported binder protocol version";
26 }
27
28 // The region is read-only: the kernel writes reply payloads into it and hands out their
29 // addresses in BR_REPLY, so it has to stay mapped for as long as the handle is used.
30 void* shared = mmap(nullptr, FF_BINDER_SHARED_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, fd, 0);
31 if (shared == MAP_FAILED) {
32 FF_DEBUG("mmap(" FF_BINDER_DEVICE ") failed: %s", strerror(errno));
33 return "Failed to mmap " FF_BINDER_DEVICE;
34 }
35
36 *binder = (FFBinder) {
37 .fd = fd,
38 .shared = (uint8_t*) shared,
39 .sharedSize = FF_BINDER_SHARED_SIZE,
40 .protocolVersion = version.protocol_version,
41 };
42 fd = -1; // ownership moved into *binder, which ffBinderClose() lets go
43 return nullptr;
44}
45
46void ffBinderClose(FFBinder* binder) {
47 if (binder->shared != nullptr) {
48 munmap(binder->shared, binder->sharedSize);
49 }
50 if (binder->fd >= 0) {
51 close(binder->fd);
52 }
53 binder->fd = -1;
54 binder->shared = nullptr;
55 binder->sharedSize = 0;
56}
57
58const char* ffBinderTransact(FFBinder* binder, uint32_t handle, uint32_t code, uint32_t flags, const FFBinderParcel* parcel, FFBinderReply* reply) {
59 reply->size = 0;
60 reply->code = 0;
61 reply->flags = 0;
62 reply->handleCount = 0;
63 reply->fdCount = 0;
64
65 if (parcel->truncated) {
66 FF_DEBUG("The parcel was truncated at %zu bytes, the caller buffer holds %zu",
67 parcel->size, parcel->capacity);
68 return "Binder parcel does not fit the caller buffer";
69 }
70
71 uint8_t writeBuffer[sizeof(uint32_t) + sizeof(struct binder_transaction_data)];
72 const struct binder_transaction_data transaction = {
73 .target = { .handle = handle },
74 .cookie = 0,
75 .code = code,
76 .flags = flags,
77 .sender_pid = 0,
78 .sender_euid = 0,
79 .data_size = parcel->size,
80 .offsets_size = 0,
81 .data = { .ptr = { .buffer = (binder_uintptr_t) (uintptr_t) parcel->data, .offsets = 0 } },
82 };
83 const uint32_t transactionCommand = BC_TRANSACTION;
84 memcpy(writeBuffer, &transactionCommand, sizeof(uint32_t));
85 memcpy(writeBuffer + sizeof(uint32_t), &transaction, sizeof(transaction));
86
87 // The read buffer only has to hold the command words and the binder_transaction_data that
88 // follows BR_REPLY; the payload itself lives in the mmap region.
89 uint8_t readBuffer[FF_BINDER_REPLY_BUFFER_SIZE];
90 struct binder_write_read exchange = {
91 .write_size = sizeof(writeBuffer),
92 .write_consumed = 0,
93 .write_buffer = (binder_uintptr_t) (uintptr_t) writeBuffer,
94 .read_size = sizeof(readBuffer),
95 .read_consumed = 0,
96 .read_buffer = (binder_uintptr_t) (uintptr_t) readBuffer,
97 };
98 if (ioctl(binder->fd, BINDER_WRITE_READ, &exchange) != 0) {
99 FF_DEBUG("BINDER_WRITE_READ(handle=%u, code=%u) failed: %s", handle, code, strerror(errno));
100 return "BINDER_WRITE_READ failed";
101 }
102
103 const uint8_t* cursor = readBuffer;
104 const uint8_t* end = readBuffer + (size_t) exchange.read_consumed;
105 while ((size_t) (end - cursor) >= sizeof(uint32_t)) {
106 uint32_t command = 0;
107 memcpy(&command, cursor, sizeof(uint32_t));
108 cursor += sizeof(uint32_t);
109
110 switch (command) {
111 case BR_NOOP:
112 case BR_TRANSACTION_COMPLETE:
113 case BR_SPAWN_LOOPER:
114 break;
115
116 case BR_FAILED_REPLY:
117 FF_DEBUG("BR_FAILED_REPLY for handle=%u, code=%u", handle, code);
118 return "Binder transaction failed";
119 case BR_DEAD_REPLY:
120 FF_DEBUG("BR_DEAD_REPLY: handle=%u is dead", handle);
121 return "Binder service is not running";
122
123 case BR_INCREFS:
124 case BR_ACQUIRE:
125 case BR_RELEASE:
126 case BR_DECREFS:
127 cursor += sizeof(struct binder_ptr_cookie);
128 break;
129 case BR_DEAD_BINDER:
130 case BR_CLEAR_DEATH_NOTIFICATION_DONE:
131 cursor += sizeof(binder_uintptr_t);
132 break;
133 case BR_ERROR:
134 cursor += sizeof(int32_t);
135 break;
136
137 case BR_REPLY: {
138 if ((size_t) (end - cursor) < sizeof(struct binder_transaction_data)) {
139 FF_DEBUG("BR_REPLY is truncated: %zu of %zu bytes left",
140 (size_t) (end - cursor), sizeof(struct binder_transaction_data));
141 return "Truncated binder reply";
142 }
143 struct binder_transaction_data data;
144 memcpy(&data, cursor, sizeof(data));
145 cursor += sizeof(data);
146
147 reply->code = data.code;
148 reply->flags = data.flags;
149
150 const uint8_t* payload = (const uint8_t*) (uintptr_t) data.data.ptr.buffer;
151 const size_t payloadSize = (size_t) data.data_size;
152 const uint8_t* offsets = (const uint8_t*) (uintptr_t) data.data.ptr.offsets;
153 const size_t offsetsSize = (size_t) data.offsets_size;
154
155 const char* error = nullptr;
156 if (payloadSize > 0) {
157 if (payloadSize > reply->capacity) {
158 FF_DEBUG("The reply carries %zu payload bytes, the caller buffer holds %zu",
159 payloadSize, reply->capacity);
160 error = "Binder reply does not fit the caller buffer";
161 } else {
162 memcpy(reply->data, payload, payloadSize);
163 reply->size = payloadSize;
164 }
165 }
166
167 // Walk the offset table for the flat objects the service sent us. A reply may carry
168 // both kinds at once and they are not interchangeable: a handle is a reference that
169 // has to be acquired (see below) to outlive the reply buffer, while a file
170 // descriptor was installed in our own fd table by the kernel and is simply ours.
171 for (size_t offset = 0; offset + sizeof(binder_size_t) <= offsetsSize; offset += sizeof(binder_size_t)) {
172 binder_size_t objectOffset = 0;
173 memcpy(&objectOffset, offsets + offset, sizeof(objectOffset));
174 if (objectOffset + sizeof(struct flat_binder_object) > payloadSize) {
175 continue;
176 }
177 struct flat_binder_object object;
178 memcpy(&object, payload + objectOffset, sizeof(object));
179 if (object.hdr.type == BINDER_TYPE_HANDLE) {
180 if (reply->handleCount < FF_BINDER_MAX_HANDLES) {
181 reply->handles[reply->handleCount++] = object.handle;
182 }
183 } else if (object.hdr.type == BINDER_TYPE_FD) {
184 if (reply->fdCount < FF_BINDER_MAX_FDS) {
185 reply->fds[reply->fdCount++] = (int32_t) object.handle;
186 } else {
187 // Nothing is going to hold this one and nothing is going to close it.
188 close((int) object.handle);
189 }
190 }
191 }
192
193 // The kernel gave us a new reference whose only strong reference belongs to the
194 // reply buffer, so it has to be acquired before that buffer is freed -- otherwise
195 // the handle silently degrades to a weak reference and every later transaction on
196 // it fails with BR_FAILED_REPLY. libbinder does the same in acquire_object().
197 uint8_t tail[FF_BINDER_MAX_HANDLES * 4 * sizeof(uint32_t) + sizeof(uint32_t) + sizeof(binder_uintptr_t)];
198 size_t tailSize = 0;
199 for (uint32_t i = 0; i < reply->handleCount; i++) {
200 const uint32_t acquire = BC_ACQUIRE;
201 const uint32_t increfs = BC_INCREFS;
202 memcpy(tail + tailSize, &acquire, sizeof(uint32_t));
203 tailSize += sizeof(uint32_t);
204 memcpy(tail + tailSize, &reply->handles[i], sizeof(uint32_t));
205 tailSize += sizeof(uint32_t);
206 memcpy(tail + tailSize, &increfs, sizeof(uint32_t));
207 tailSize += sizeof(uint32_t);
208 memcpy(tail + tailSize, &reply->handles[i], sizeof(uint32_t));
209 tailSize += sizeof(uint32_t);
210 }
211 const uint32_t freeCommand = BC_FREE_BUFFER;
212 const binder_uintptr_t freeBuffer = data.data.ptr.buffer;
213 memcpy(tail + tailSize, &freeCommand, sizeof(uint32_t));
214 tailSize += sizeof(uint32_t);
215 memcpy(tail + tailSize, &freeBuffer, sizeof(freeBuffer));
216 tailSize += sizeof(freeBuffer);
217
218 struct binder_write_read release = {
219 .write_size = tailSize,
220 .write_consumed = 0,
221 .write_buffer = (binder_uintptr_t) (uintptr_t) tail,
222 .read_size = 0,
223 .read_consumed = 0,
224 .read_buffer = 0,
225 };
226 // This one ioctl carries the whole tail, so its failure is not cosmetic: without
227 // BC_ACQUIRE the handles handed to the caller are only weakly referenced and every
228 // later transaction on them fails with BR_FAILED_REPLY, which says nothing about the
229 // real cause. Reported, but only when nothing more specific is already at hand.
230 if (ioctl(binder->fd, BINDER_WRITE_READ, &release) != 0 && error == nullptr) {
231 FF_DEBUG("BC_ACQUIRE/BC_FREE_BUFFER failed: %s", strerror(errno));
232 error = "Releasing the binder reply failed";
233 }
234
235 return error;
236 }
237
238 default:
239 FF_DEBUG("Unexpected binder command 0x%08x for handle=%u, code=%u", command, handle, code);
240 return "Unexpected binder command";
241 }
242 }
243
244 FF_DEBUG("The binder reply for handle=%u, code=%u held no BR_REPLY (%zu bytes read)",
245 handle, code, (size_t) exchange.read_consumed);
246 return "No reply from binder";
247}
248
249const char* ffBinderLookupService(FFBinder* binder, const char* name, uint32_t transactionCode, uint32_t* handle) {
250 uint8_t parcelBuffer[256];
251 FFBinderParcel parcel = ffBinderParcelCreate(parcelBuffer, sizeof(parcelBuffer));
252 ffBinderParcelPutInterfaceToken(&parcel, FF_BINDER_SM_DESCRIPTOR);
253 ffBinderParcelPutString16(&parcel, name);
254
255 uint8_t replyBuffer[256];
256 FFBinderReply reply = ffBinderReplyCreate(replyBuffer, sizeof(replyBuffer));
257 const char* error = ffBinderTransact(binder, FF_BINDER_SERVICE_MANAGER_HANDLE, transactionCode, 0, &parcel, &reply);
258 if (error != nullptr) {
259 return error;
260 }
261 if (ffBinderReplyIsStatus(&reply)) {
262 FF_DEBUG("The service manager returned a status parcel for %s, code=%u", name, transactionCode);
263 return "Service manager rejected the request";
264 }
265 const int32_t exception = ffBinderReadI32(reply.data, reply.size, 0);
266 if (exception != 0) {
267 FF_DEBUG("The service manager raised exception %d for %s", exception, name);
268 return "Service manager raised an exception";
269 }
270 if (reply.handleCount == 0) {
271 FF_DEBUG("The service manager returned no handle for %s", name);
272 return "Service is not registered";
273 }
274
275 *handle = reply.handles[0];
276 return nullptr;
277}
278
279void ffBinderServiceHandleRelease(FFBinderServiceHandle* service) {
280 if (service->binder == nullptr || service->handle == 0) {
281 return;
282 }
283
284 // The mirror of the acquire at the end of ffBinderTransact(): one strong and one weak reference
285 // each, dropped in the same order they were taken. The kernel keeps the service node alive until
286 // the last one goes, so this is what stops a short-lived fastfetch from holding one for the whole
287 // of its life.
288 const uint32_t commands[] = { BC_RELEASE, BC_DECREFS };
289 uint8_t tail[2 * 2 * sizeof(uint32_t)];
290 size_t tailSize = 0;
291 for (size_t i = 0; i < 2; i++) {
292 memcpy(tail + tailSize, &commands[i], sizeof(uint32_t));
293 tailSize += sizeof(uint32_t);
294 memcpy(tail + tailSize, &service->handle, sizeof(uint32_t));
295 tailSize += sizeof(uint32_t);
296 }
297
298 struct binder_write_read release = {
299 .write_size = tailSize,
300 .write_consumed = 0,
301 .write_buffer = (binder_uintptr_t) (uintptr_t) tail,
302 .read_size = 0,
303 .read_consumed = 0,
304 .read_buffer = 0,
305 };
306 // Not reported: this runs from a cleanup attribute, where there is no caller left to report to,
307 // and the only way for it to fail is a binder that is already dead -- which every earlier call on
308 // the same binder would have said already.
309 ioctl(service->binder->fd, BINDER_WRITE_READ, &release);
310 service->handle = 0;
311}
312
313void ffBinderAcquiredRelease(FFBinderAcquired* acquired) {
314 if (acquired->replied == nullptr || *acquired->replied == false) {
315 return; // nothing was acquired
316 }
317
318 // A descriptor is not a reference: the kernel installed it in our own fd table when it delivered
319 // the reply, and nobody else holds it, so closing it is the whole of giving it back.
320 for (uint32_t i = 0; i < acquired->fdCount; i++) {
321 close(acquired->fds[i]);
322 }
323
324 // The mirror of the acquire the transaction appended to its tail, in the same order it took them.
325 // No binder to talk through means the handles went with it when it was closed.
326 if (acquired->binder == nullptr || acquired->binder->fd < 0) {
327 return;
328 }
329
330 for (uint32_t i = 0; i < acquired->handleCount; i++) {
331 FFBinderServiceHandle handle = { .binder = acquired->binder, .handle = acquired->handles[i] };
332 ffBinderServiceHandleRelease(&handle);
333 }
334}