/* consumestr: consumes a C string from a buffer @buf of length @length * @buf Buffer to consume * @length Length of buffer * * Returns a pointer to the base of the string, or NULL for errors. */ char *consumestr(char **buf, size_t *buflength) { size_t len = strnlen(*buf, *buflength); if (len == *buflength) /* There's no null-terminator */ return NULL; return consumebytes(len + 1, buf, buflength); }
// Gets the next "bytes" bytes, regardless of whether they are available in the // current buffer or not. Returns a status code as described in decoder.int.h. FORCEINLINE int32_t getbytes(upb_pbdecoder *d, void *buf, size_t bytes) { if (curbufleft(d) >= bytes) { // Buffer has enough data to satisfy. consumebytes(d, buf, bytes); return DECODE_OK; } else { return getbytes_slow(d, buf, bytes); } }
// Slow path for getting the next "bytes" bytes, regardless of whether they are // available in the current buffer or not. Returns a status code as described // in decoder.int.h. static NOINLINE int32_t getbytes_slow(upb_pbdecoder *d, void *buf, size_t bytes) { const size_t avail = curbufleft(d); consumebytes(d, buf, avail); bytes -= avail; assert(bytes > 0); if (in_residual_buf(d, d->ptr)) { advancetobuf(d, d->buf_param, d->size_param); } if (curbufleft(d) >= bytes) { consumebytes(d, buf + avail, bytes); return DECODE_OK; } else if (d->data_end == d->delim_end) { seterr(d, "Submessage ended in the middle of a value or group"); return upb_pbdecoder_suspend(d); } else { return suspend_save(d); } }
int minijail_unmarshal(struct minijail *j, char *serialized, size_t length) { int i; int count; int ret = -EINVAL; if (length < sizeof(*j)) goto out; memcpy((void *)j, serialized, sizeof(*j)); serialized += sizeof(*j); length -= sizeof(*j); /* Potentially stale pointers not used as signals. */ j->bindings_head = NULL; j->bindings_tail = NULL; j->filter_prog = NULL; if (j->user) { /* stale pointer */ char *user = consumestr(&serialized, &length); if (!user) goto clear_pointers; j->user = strdup(user); if (!j->user) goto clear_pointers; } if (j->chrootdir) { /* stale pointer */ char *chrootdir = consumestr(&serialized, &length); if (!chrootdir) goto bad_chrootdir; j->chrootdir = strdup(chrootdir); if (!j->chrootdir) goto bad_chrootdir; } if (j->chdir) { /* stale pointer */ char *chdirstr = consumestr(&serialized, &length); if (!chdirstr) goto bad_chdir; j->chdir = strdup(chdirstr); if (!j->chdir) goto bad_chdir; } if (j->flags.seccomp_filter && j->filter_len > 0) { size_t ninstrs = j->filter_len; if (ninstrs > (SIZE_MAX / sizeof(struct sock_filter)) || ninstrs > USHRT_MAX) goto bad_filters; size_t program_len = ninstrs * sizeof(struct sock_filter); void *program = consumebytes(program_len, &serialized, &length); if (!program) goto bad_filters; j->filter_prog = malloc(sizeof(struct sock_fprog)); j->filter_prog->len = ninstrs; j->filter_prog->filter = malloc(program_len); memcpy(j->filter_prog->filter, program, program_len); } if (j->meta_file) { j->meta_file = NULL; } count = j->binding_count; j->binding_count = 0; for (i = 0; i < count; ++i) { int *writeable; const char *dest; const char *src = consumestr(&serialized, &length); if (!src) goto bad_bindings; dest = consumestr(&serialized, &length); if (!dest) goto bad_bindings; writeable = consumebytes(sizeof(*writeable), &serialized, &length); if (!writeable) goto bad_bindings; if (minijail_bind(j, src, dest, *writeable)) goto bad_bindings; } close(j->syscall_pipe_fds[0]); /* read endpoint */ j->syscall_pipe_fds[0] = -1; signum_fd = j->syscall_pipe_fds[1]; return 0; bad_bindings: if (j->flags.seccomp_filter && j->filter_len > 0) { free(j->filter_prog->filter); free(j->filter_prog); } bad_filters: if (j->chrootdir) free(j->chrootdir); bad_chdir: if (j->chdir) free(j->chdir); bad_chrootdir: if (j->user) free(j->user); clear_pointers: j->user = NULL; j->chrootdir = NULL; j->chdir = NULL; out: return ret; }