• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "com_android_internal_os_Zygote.h"
18 
19 #include <algorithm>
20 #include <android-base/logging.h>
21 #include <async_safe/log.h>
22 #include <cctype>
23 #include <chrono>
24 #include <core_jni_helpers.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <jni.h>
28 #include <nativehelper/JNIHelp.h>
29 #include <optional>
30 #include <poll.h>
31 #include <unistd.h>
32 #include <utility>
33 #include <utils/misc.h>
34 #include <sys/mman.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <vector>
38 
39 namespace android {
40 
41 using namespace std::placeholders;
42 using android::base::StringPrintf;
43 using android::zygote::ZygoteFailure;
44 
45 // WARNING: Knows a little about the wire protocol used to communicate with Zygote.
46 // TODO: Fix error handling.
47 
48 constexpr size_t MAX_COMMAND_BYTES = 12200;
49 constexpr size_t NICE_NAME_BYTES = 50;
50 
51 // A buffer optionally bundled with a file descriptor from which we can fill it.
52 // Does not own the file descriptor; destroying a NativeCommandBuffer does not
53 // close the descriptor.
54 class NativeCommandBuffer {
55  public:
NativeCommandBuffer(int sourceFd)56   NativeCommandBuffer(int sourceFd): mEnd(0), mNext(0), mLinesLeft(0), mFd(sourceFd) {}
57 
58   // Read mNext line from mFd, filling mBuffer from file descriptor, as needed.
59   // Return a pair of pointers pointing to the first character, and one past the
60   // mEnd of the line, i.e. at the newline. Returns nothing on failure.
61   template<class FailFn>
readLine(FailFn fail_fn)62   std::optional<std::pair<char*, char*>> readLine(FailFn fail_fn) {
63     char* result = mBuffer + mNext;
64     while (true) {
65       if (mNext == mEnd) {
66         if (mEnd == MAX_COMMAND_BYTES) {
67           return {};
68         }
69         if (mFd == -1) {
70           fail_fn("ZygoteCommandBuffer.readLine attempted to read from mFd -1");
71         }
72         ssize_t nread = TEMP_FAILURE_RETRY(read(mFd, mBuffer + mEnd, MAX_COMMAND_BYTES - mEnd));
73         if (nread <= 0) {
74           if (nread == 0) {
75             return {};
76           }
77           fail_fn(CREATE_ERROR("session socket read failed: %s", strerror(errno)));
78         } else if (nread == MAX_COMMAND_BYTES - mEnd) {
79           // This is pessimistic by one character, but close enough.
80           fail_fn("ZygoteCommandBuffer overflowed: command too long");
81         }
82         mEnd += nread;
83       }
84       // UTF-8 does not allow newline to occur as part of a multibyte character.
85       char* nl = static_cast<char *>(memchr(mBuffer + mNext, '\n', mEnd - mNext));
86       if (nl == nullptr) {
87         mNext = mEnd;
88       } else {
89         mNext = nl - mBuffer + 1;
90         if (--mLinesLeft < 0) {
91           fail_fn("ZygoteCommandBuffer.readLine attempted to read past mEnd of command");
92         }
93         return std::make_pair(result, nl);
94       }
95     }
96   }
97 
reset()98   void reset() {
99     mNext = 0;
100   }
101 
102   // Make sure the current command is fully buffered, without reading past the current command.
103   template<class FailFn>
readAllLines(FailFn fail_fn)104   void readAllLines(FailFn fail_fn) {
105      while (mLinesLeft > 0) {
106        readLine(fail_fn);
107     }
108   }
109 
clear()110   void clear() {
111     // Don't bother to actually clear the buffer; it'll be unmapped in the child anyway.
112     reset();
113     mNiceName[0] = '\0';
114     mEnd = 0;
115   }
116 
117   // Insert line into the mBuffer. Checks that the mBuffer is not associated with an mFd.
118   // Implicitly adds newline separators. Allows mBuffer contents to be explicitly set.
insert(const char * line,size_t lineLen)119   void insert(const char* line, size_t lineLen) {
120     DCHECK(mFd == -1);
121     CHECK(mEnd + lineLen < MAX_COMMAND_BYTES);
122     strncpy(mBuffer + mEnd, line, lineLen);
123     mBuffer[mEnd + lineLen] = '\n';
124     mEnd += lineLen + 1;
125   }
126 
127   // Clear mBuffer, start reading new command, return the number of arguments, leaving mBuffer
128   // positioned at the beginning of first argument. Return 0 on EOF.
129   template<class FailFn>
getCount(FailFn fail_fn)130   int getCount(FailFn fail_fn) {
131     mLinesLeft = 1;
132     auto line = readLine(fail_fn);
133     if (!line.has_value()) {
134       return 0;
135     }
136     char* countString = line.value().first;  // Newline terminated.
137     long nArgs = atol(countString);
138     if (nArgs <= 0 || nArgs >= MAX_COMMAND_BYTES / 2) {
139       fail_fn(CREATE_ERROR("Unreasonable argument count %ld", nArgs));
140     }
141     mLinesLeft = nArgs;
142     return static_cast<int>(nArgs);
143   }
144 
145   // Is the mBuffer a simple fork command?
146   // We disallow request to wrap the child process, child zygotes, anything that
147   // mentions capabilities or requests uid < minUid.
148   // We insist that --setuid and --setgid arguments are explicitly included and that the
149   // command starts with --runtime-args.
150   // Assumes we are positioned at the beginning of the command after the argument count,
151   // and leaves the position at some indeterminate position in the buffer.
152   // As a side effect, this sets mNiceName to a non-empty string, if possible.
153   template<class FailFn>
isSimpleForkCommand(int minUid,FailFn fail_fn)154   bool isSimpleForkCommand(int minUid, FailFn fail_fn) {
155     if (mLinesLeft <= 0 || mLinesLeft  >= MAX_COMMAND_BYTES / 2) {
156       return false;
157     }
158     static const char* RUNTIME_ARGS = "--runtime-args";
159     static const char* INVOKE_WITH = "--invoke-with";
160     static const char* CHILD_ZYGOTE = "--start-child-zygote";
161     static const char* SETUID = "--setuid=";
162     static const char* SETGID = "--setgid=";
163     static const char* CAPABILITIES = "--capabilities";
164     static const char* NICE_NAME = "--nice-name=";
165     static const size_t RA_LENGTH = strlen(RUNTIME_ARGS);
166     static const size_t IW_LENGTH = strlen(INVOKE_WITH);
167     static const size_t CZ_LENGTH = strlen(CHILD_ZYGOTE);
168     static const size_t SU_LENGTH = strlen(SETUID);
169     static const size_t SG_LENGTH = strlen(SETGID);
170     static const size_t CA_LENGTH = strlen(CAPABILITIES);
171     static const size_t NN_LENGTH = strlen(NICE_NAME);
172 
173     bool saw_setuid = false, saw_setgid = false;
174     bool saw_runtime_args = false;
175 
176     while (mLinesLeft > 0) {
177       auto read_result = readLine(fail_fn);
178       if (!read_result.has_value()) {
179         return false;
180       }
181       auto [arg_start, arg_end] = read_result.value();
182       if (arg_end - arg_start == RA_LENGTH
183           && strncmp(arg_start, RUNTIME_ARGS, RA_LENGTH) == 0) {
184         saw_runtime_args = true;
185         continue;
186       }
187       if (arg_end - arg_start >= NN_LENGTH
188           && strncmp(arg_start, NICE_NAME, NN_LENGTH) == 0) {
189         size_t name_len = arg_end - (arg_start + NN_LENGTH);
190         size_t copy_len = std::min(name_len, NICE_NAME_BYTES - 1);
191         memcpy(mNiceName, arg_start + NN_LENGTH, copy_len);
192         mNiceName[copy_len] = '\0';
193         continue;
194       }
195       if (arg_end - arg_start == IW_LENGTH
196           && strncmp(arg_start, INVOKE_WITH, IW_LENGTH) == 0) {
197         // This also removes the need for invoke-with security checks here.
198         return false;
199       }
200       if (arg_end - arg_start == CZ_LENGTH
201           && strncmp(arg_start, CHILD_ZYGOTE, CZ_LENGTH) == 0) {
202         return false;
203       }
204       if (arg_end - arg_start >= CA_LENGTH
205           && strncmp(arg_start, CAPABILITIES, CA_LENGTH) == 0) {
206         return false;
207       }
208       if (arg_end - arg_start >= SU_LENGTH
209           && strncmp(arg_start, SETUID, SU_LENGTH) == 0) {
210         int uid = digitsVal(arg_start + SU_LENGTH, arg_end);
211         if (uid < minUid) {
212           return false;
213         }
214         saw_setuid = true;
215         continue;
216       }
217       if (arg_end - arg_start >= SG_LENGTH
218           && strncmp(arg_start, SETGID, SG_LENGTH) == 0) {
219         int gid = digitsVal(arg_start + SG_LENGTH, arg_end);
220         if (gid == -1) {
221           return false;
222         }
223         saw_setgid = true;
224       }
225     }
226     return saw_runtime_args && saw_setuid && saw_setgid;
227   }
228 
setFd(int new_fd)229   void setFd(int new_fd) {
230     mFd = new_fd;
231   }
232 
getFd() const233   int getFd() const {
234     return mFd;
235   }
236 
niceNameAddr() const237   const char* niceNameAddr() const {
238     return mNiceName;
239   }
240 
241   // Debug only:
logState() const242   void logState() const {
243     ALOGD("mbuffer starts with %c%c, nice name is %s, "
244           "mEnd = %u, mNext = %u, mLinesLeft = %d, mFd = %d",
245           mBuffer[0], (mBuffer[1] == '\n' ? ' ' : mBuffer[1]),
246           niceNameAddr(),
247           static_cast<unsigned>(mEnd), static_cast<unsigned>(mNext),
248           static_cast<int>(mLinesLeft), mFd);
249   }
250 
251  private:
252   // Picky version of atoi(). No sign or unexpected characters allowed. Return -1 on failure.
digitsVal(char * start,char * end)253   static int digitsVal(char* start, char* end) {
254     int result = 0;
255     if (end - start > 6) {
256       return -1;
257     }
258     for (char* dp = start; dp < end; ++dp) {
259       if (*dp < '0' || *dp > '9') {
260         ALOGW("Argument failed integer format check");
261         return -1;
262       }
263       result = 10 * result + (*dp - '0');
264     }
265     return result;
266   }
267 
268   uint32_t mEnd;  // Index of first empty byte in the mBuffer.
269   uint32_t mNext;  // Index of first character past last line returned by readLine.
270   int32_t mLinesLeft;  // Lines in current command that haven't yet been read.
271   int mFd;  // Open file descriptor from which we can read more. -1 if none.
272   char mNiceName[NICE_NAME_BYTES];
273   char mBuffer[MAX_COMMAND_BYTES];
274 };
275 
276 static_assert(sizeof(NativeCommandBuffer) < 3 * 4096);
277 
278 static int buffersAllocd(0);
279 
280 // Get a new NativeCommandBuffer. Can only be called once between freeNativeBuffer calls,
281 // so that only one buffer exists at a time.
com_android_internal_os_ZygoteCommandBuffer_getNativeBuffer(JNIEnv * env,jclass,jint fd)282 jlong com_android_internal_os_ZygoteCommandBuffer_getNativeBuffer(JNIEnv* env, jclass, jint fd) {
283   CHECK(buffersAllocd == 0);
284   ++buffersAllocd;
285   // MMap explicitly to get it page aligned.
286   void *bufferMem = mmap(NULL, sizeof(NativeCommandBuffer), PROT_READ | PROT_WRITE,
287                          MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0);
288   // Currently we mmap and unmap one for every request handled by the Java code.
289   // That could be improved, but unclear it matters.
290   if (bufferMem == MAP_FAILED) {
291     ZygoteFailure(env, nullptr, nullptr, "Failed to map argument buffer");
292   }
293   return (jlong) new(bufferMem) NativeCommandBuffer(fd);
294 }
295 
296 // Delete native command buffer.
com_android_internal_os_ZygoteCommandBuffer_freeNativeBuffer(JNIEnv * env,jclass,jlong j_buffer)297 void com_android_internal_os_ZygoteCommandBuffer_freeNativeBuffer(JNIEnv* env, jclass,
298                                                                   jlong j_buffer) {
299   CHECK(buffersAllocd == 1);
300   NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
301   n_buffer->~NativeCommandBuffer();
302   if (munmap(n_buffer, sizeof(NativeCommandBuffer)) != 0) {
303     ZygoteFailure(env, nullptr, nullptr, "Failed to unmap argument buffer");
304   }
305   --buffersAllocd;
306 }
307 
308 // Clear the buffer, read the line containing the count, and return the count.
com_android_internal_os_ZygoteCommandBuffer_nativeGetCount(JNIEnv * env,jclass,jlong j_buffer)309 jint com_android_internal_os_ZygoteCommandBuffer_nativeGetCount(JNIEnv* env, jclass,
310                                                                 jlong j_buffer) {
311   NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
312   auto fail_fn = std::bind(ZygoteFailure, env, nullptr, nullptr, _1);
313   return n_buffer->getCount(fail_fn);
314 }
315 
316 // Explicitly insert a string as the last line (argument) of the buffer.
com_android_internal_os_ZygoteCommandBuffer_insert(JNIEnv * env,jclass,jlong j_buffer,jstring line)317 void com_android_internal_os_ZygoteCommandBuffer_insert(JNIEnv* env, jclass, jlong j_buffer,
318                                                         jstring line) {
319   NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
320   size_t lineLen = static_cast<size_t>(env->GetStringUTFLength(line));
321   const char* cstring = env->GetStringUTFChars(line, NULL);
322   n_buffer->insert(cstring, lineLen);
323   env->ReleaseStringUTFChars(line, cstring);
324 }
325 
326 // Read a line from the buffer, refilling as necessary.
com_android_internal_os_ZygoteCommandBuffer_nativeNextArg(JNIEnv * env,jclass,jlong j_buffer)327 jstring com_android_internal_os_ZygoteCommandBuffer_nativeNextArg(JNIEnv* env, jclass,
328                                                                   jlong j_buffer) {
329   NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
330   auto fail_fn = std::bind(ZygoteFailure, env, n_buffer->niceNameAddr(), nullptr, _1);
331   auto line = n_buffer->readLine(fail_fn);
332   if (!line.has_value()) {
333     fail_fn("Incomplete zygote command");
334   }
335   auto [cresult, endp] = line.value();
336   // OK to temporarily clobber the buffer, since this is not thread safe, and we're modifying
337   // the buffer anyway.
338   *endp = '\0';
339   jstring result = env->NewStringUTF(cresult);
340   *endp = '\n';
341   return result;
342 }
343 
344 // Read all lines from the current command into the buffer, and then reset the buffer, so
345 // we will start reading again at the beginning of the command, starting with the argument
346 // count. And we don't need access to the fd to do so.
com_android_internal_os_ZygoteCommandBuffer_nativeReadFullyAndReset(JNIEnv * env,jclass,jlong j_buffer)347 void com_android_internal_os_ZygoteCommandBuffer_nativeReadFullyAndReset(JNIEnv* env, jclass,
348                                                                          jlong j_buffer) {
349   NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
350   auto fail_fn = std::bind(ZygoteFailure, env, n_buffer->niceNameAddr(), nullptr, _1);
351   n_buffer->readAllLines(fail_fn);
352   n_buffer->reset();
353 }
354 
355 // Fork a child as specified by the current command buffer, and refill the command
356 // buffer from the given socket. So long as the result is another simple fork command,
357 // repeat this process.
358 // It must contain a fork command, which is currently restricted not to fork another
359 // zygote or involve a wrapper process.
360 // The initial buffer should be partially or entirely read; we read it fully and reset it.
361 // When we return, the buffer contains the command we couldn't handle, and has been reset().
362 // We return false in the parent when we see a command we didn't understand, and thus the
363 // command in the buffer still needs to be executed.
364 // We return true in each child.
365 // We only process fork commands if the peer uid matches expected_uid.
366 // For every fork command after the first, we check that the requested uid is at
367 // least minUid.
com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly(JNIEnv * env,jclass,jlong j_buffer,jint zygote_socket_fd,jint expected_uid,jint minUid,jstring managed_nice_name)368 jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly(
369             JNIEnv* env,
370             jclass,
371             jlong j_buffer,
372             jint zygote_socket_fd,
373             jint expected_uid,
374             jint minUid,
375             jstring managed_nice_name) {
376 
377   NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
378   int session_socket = n_buffer->getFd();
379   std::vector<int> session_socket_fds {session_socket};
380   auto fail_fn_1 = std::bind(ZygoteFailure, env, static_cast<const char*>(nullptr),
381                              static_cast<jstring>(managed_nice_name), _1);
382   // This binds to the nice name address; the actual names are updated by isSimpleForkCommand:
383   auto fail_fn_n = std::bind(ZygoteFailure, env, n_buffer->niceNameAddr(),
384                              static_cast<jstring>(nullptr), _1);
385   auto fail_fn_z = std::bind(ZygoteFailure, env, "zygote", nullptr, _1);
386 
387   struct pollfd fd_structs[2];
388   static const int ZYGOTE_IDX = 0;
389   static const int SESSION_IDX = 1;
390   fd_structs[ZYGOTE_IDX].fd = zygote_socket_fd;
391   fd_structs[ZYGOTE_IDX].events = POLLIN;
392   fd_structs[SESSION_IDX].fd = session_socket;
393   fd_structs[SESSION_IDX].events = POLLIN;
394 
395   struct timeval timeout;
396   socklen_t timeout_size = sizeof timeout;
397   if (getsockopt(session_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, &timeout_size) != 0) {
398     fail_fn_z("Failed to retrieve session socket timeout");
399   }
400 
401   struct ucred credentials;
402   socklen_t cred_size = sizeof credentials;
403   if (getsockopt(n_buffer->getFd(), SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1
404       || cred_size != sizeof credentials) {
405     fail_fn_1(CREATE_ERROR("ForkMany failed to get initial credentials, %s", strerror(errno)));
406   }
407 
408   bool first_time = true;
409   do {
410     if (credentials.uid != expected_uid) {
411       return JNI_FALSE;
412     }
413     n_buffer->readAllLines(first_time ? fail_fn_1 : fail_fn_n);
414     n_buffer->reset();
415     int pid = zygote::forkApp(env, /* no pipe FDs */ -1, -1, session_socket_fds,
416                               /*args_known=*/ true, /*is_priority_fork=*/ true,
417                               /*purge=*/ first_time);
418     if (pid == 0) {
419       return JNI_TRUE;
420     }
421     // We're in the parent. Write big-endian pid, followed by a boolean.
422     char pid_buf[5];
423     int tmp_pid = pid;
424     for (int i = 3; i >= 0; --i) {
425       pid_buf[i] = tmp_pid & 0xff;
426       tmp_pid >>= 8;
427     }
428     pid_buf[4] = 0;  // Process is not wrapped.
429     int res = TEMP_FAILURE_RETRY(write(session_socket, pid_buf, 5));
430     if (res != 5) {
431       if (res == -1) {
432         (first_time ? fail_fn_1 : fail_fn_n)
433             (CREATE_ERROR("Pid write error %d: %s", errno, strerror(errno)));
434       } else {
435         (first_time ? fail_fn_1 : fail_fn_n)
436             (CREATE_ERROR("Write unexpectedly returned short: %d < 5", res));
437       }
438     }
439     // Clear buffer and get count from next command.
440     n_buffer->clear();
441     for (;;) {
442       // Poll isn't strictly necessary for now. But without it, disconnect is hard to detect.
443       int poll_res = TEMP_FAILURE_RETRY(poll(fd_structs, 2, -1 /* infinite timeout */));
444       if ((fd_structs[SESSION_IDX].revents & POLLIN) != 0) {
445         if (n_buffer->getCount(fail_fn_z) != 0) {
446           break;
447         }  // else disconnected;
448       } else if (poll_res == 0 || (fd_structs[ZYGOTE_IDX].revents & POLLIN) == 0) {
449         fail_fn_z(
450             CREATE_ERROR("Poll returned with no descriptors ready! Poll returned %d", poll_res));
451       }
452       // We've now seen either a disconnect or connect request.
453       close(session_socket);
454       int new_fd = TEMP_FAILURE_RETRY(accept(zygote_socket_fd, nullptr, nullptr));
455       if (new_fd == -1) {
456         fail_fn_z(CREATE_ERROR("Accept(%d) failed: %s", zygote_socket_fd, strerror(errno)));
457       }
458       if (new_fd != session_socket) {
459           // Move new_fd back to the old value, so that we don't have to change Java-level data
460           // structures to reflect a change. This implicitly closes the old one.
461           if (TEMP_FAILURE_RETRY(dup2(new_fd, session_socket)) != session_socket) {
462             fail_fn_z(CREATE_ERROR("Failed to move fd %d to %d: %s",
463                                    new_fd, session_socket, strerror(errno)));
464           }
465           close(new_fd);  //  On Linux, fd is closed even if EINTR is returned.
466       }
467       // If we ever return, we effectively reuse the old Java ZygoteConnection.
468       // None of its state needs to change.
469       if (setsockopt(session_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, timeout_size) != 0) {
470         fail_fn_z(CREATE_ERROR("Failed to set receive timeout for socket %d: %s",
471                                session_socket, strerror(errno)));
472       }
473       if (setsockopt(session_socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, timeout_size) != 0) {
474         fail_fn_z(CREATE_ERROR("Failed to set send timeout for socket %d: %s",
475                                session_socket, strerror(errno)));
476       }
477       if (getsockopt(session_socket, SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1) {
478         fail_fn_z(CREATE_ERROR("ForkMany failed to get credentials: %s", strerror(errno)));
479       }
480       if (cred_size != sizeof credentials) {
481         fail_fn_z(CREATE_ERROR("ForkMany credential size = %d, should be %d",
482                                cred_size, static_cast<int>(sizeof credentials)));
483       }
484     }
485     first_time = false;
486   } while (n_buffer->isSimpleForkCommand(minUid, fail_fn_n));
487   ALOGW("forkRepeatedly terminated due to non-simple command");
488   n_buffer->logState();
489   n_buffer->reset();
490   return JNI_FALSE;
491 }
492 
493 #define METHOD_NAME(m) com_android_internal_os_ZygoteCommandBuffer_ ## m
494 
495 static const JNINativeMethod gMethods[] = {
496         {"getNativeBuffer", "(I)J", (void *) METHOD_NAME(getNativeBuffer)},
497         {"freeNativeBuffer", "(J)V", (void *) METHOD_NAME(freeNativeBuffer)},
498         {"insert", "(JLjava/lang/String;)V", (void *) METHOD_NAME(insert)},
499         {"nativeNextArg", "(J)Ljava/lang/String;", (void *) METHOD_NAME(nativeNextArg)},
500         {"nativeReadFullyAndReset", "(J)V", (void *) METHOD_NAME(nativeReadFullyAndReset)},
501         {"nativeGetCount", "(J)I", (void *) METHOD_NAME(nativeGetCount)},
502         {"nativeForkRepeatedly", "(JIIILjava/lang/String;)Z",
503           (void *) METHOD_NAME(nativeForkRepeatedly)},
504 };
505 
register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv * env)506 int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv* env) {
507   return RegisterMethodsOrDie(env, "com/android/internal/os/ZygoteCommandBuffer", gMethods,
508                               NELEM(gMethods));
509 }
510 
511 }  // namespace android
512