|  | /* | 
|  | * This file is part of the UCB release of Plan 9. It is subject to the license | 
|  | * terms in the LICENSE file found in the top-level directory of this | 
|  | * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
|  | * part of the UCB release of Plan 9, including this file, may be copied, | 
|  | * modified, propagated, or distributed except according to the terms contained | 
|  | * in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include <string.h> | 
|  | #include <fcall.h> | 
|  |  | 
|  | static uint8_t *gstring(uint8_t * p, uint8_t * ep, char **s) | 
|  | { | 
|  | unsigned int n; | 
|  |  | 
|  | if (p + BIT16SZ > ep) | 
|  | return NULL; | 
|  | n = GBIT16(p); | 
|  | p += BIT16SZ - 1; | 
|  | if (p + n + 1 > ep) | 
|  | return NULL; | 
|  | /* move it down, on top of count, to make room for '\0' */ | 
|  | memmove(p, p + 1, n); | 
|  | p[n] = '\0'; | 
|  | *s = (char *)p; | 
|  | p += n + 1; | 
|  | return p; | 
|  | } | 
|  |  | 
|  | static uint8_t *gqid(uint8_t * p, uint8_t * ep, struct qid *q) | 
|  | { | 
|  | if (p + QIDSZ > ep) | 
|  | return NULL; | 
|  | q->type = GBIT8(p); | 
|  | p += BIT8SZ; | 
|  | q->vers = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | q->path = GBIT64(p); | 
|  | p += BIT64SZ; | 
|  | return p; | 
|  | } | 
|  |  | 
|  | void init_empty_dir(struct dir *d) | 
|  | { | 
|  | d->type = ~0; | 
|  | d->dev = ~0; | 
|  | d->qid.path = ~0; | 
|  | d->qid.vers = ~0; | 
|  | d->qid.type = ~0; | 
|  | d->mode = ~0; | 
|  | d->atime = ~0; | 
|  | d->mtime = ~0; | 
|  | d->length = ~0; | 
|  | d->name = ""; | 
|  | d->uid = ""; | 
|  | d->gid = ""; | 
|  | d->muid = ""; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * no syntactic checks. | 
|  | * three causes for error: | 
|  | *  1. message size field is incorrect | 
|  | *  2. input buffer too short for its own data (counts too long, etc.) | 
|  | *  3. too many names or qids | 
|  | * gqid() and gstring() return NULL if they would reach beyond buffer. | 
|  | * main switch statement checks range and also can fall through | 
|  | * to test at end of routine. | 
|  | */ | 
|  | unsigned int convM2S(uint8_t * ap, unsigned int nap, struct fcall *f) | 
|  | { | 
|  | uint8_t *p, *ep; | 
|  | unsigned int i, size; | 
|  |  | 
|  | p = ap; | 
|  | ep = p + nap; | 
|  |  | 
|  | if (p + BIT32SZ + BIT8SZ + BIT16SZ > ep) | 
|  | return 0; | 
|  | size = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  |  | 
|  | if (size < BIT32SZ + BIT8SZ + BIT16SZ) | 
|  | return 0; | 
|  |  | 
|  | f->type = GBIT8(p); | 
|  | p += BIT8SZ; | 
|  | f->tag = GBIT16(p); | 
|  | p += BIT16SZ; | 
|  |  | 
|  | switch (f->type) { | 
|  | default: | 
|  | return 0; | 
|  |  | 
|  | case Tversion: | 
|  | if (p + BIT32SZ > ep) | 
|  | return 0; | 
|  | f->msize = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | p = gstring(p, ep, &f->version); | 
|  | break; | 
|  |  | 
|  | case Tflush: | 
|  | if (p + BIT16SZ > ep) | 
|  | return 0; | 
|  | f->oldtag = GBIT16(p); | 
|  | p += BIT16SZ; | 
|  | break; | 
|  |  | 
|  | case Tauth: | 
|  | if (p + BIT32SZ > ep) | 
|  | return 0; | 
|  | f->afid = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | p = gstring(p, ep, &f->uname); | 
|  | if (p == NULL) | 
|  | break; | 
|  | p = gstring(p, ep, &f->aname); | 
|  | if (p == NULL) | 
|  | break; | 
|  | break; | 
|  |  | 
|  | case Tattach: | 
|  | if (p + BIT32SZ > ep) | 
|  | return 0; | 
|  | f->fid = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | if (p + BIT32SZ > ep) | 
|  | return 0; | 
|  | f->afid = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | p = gstring(p, ep, &f->uname); | 
|  | if (p == NULL) | 
|  | break; | 
|  | p = gstring(p, ep, &f->aname); | 
|  | if (p == NULL) | 
|  | break; | 
|  | break; | 
|  |  | 
|  | case Twalk: | 
|  | if (p + BIT32SZ + BIT32SZ + BIT16SZ > ep) | 
|  | return 0; | 
|  | f->fid = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | f->newfid = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | f->nwname = GBIT16(p); | 
|  | p += BIT16SZ; | 
|  | if (f->nwname > MAXWELEM) | 
|  | return 0; | 
|  | for (i = 0; i < f->nwname; i++) { | 
|  | p = gstring(p, ep, &f->wname[i]); | 
|  | if (p == NULL) | 
|  | break; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case Topen: | 
|  | if (p + BIT32SZ + BIT8SZ > ep) | 
|  | return 0; | 
|  | f->fid = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | f->mode = GBIT8(p); | 
|  | p += BIT8SZ; | 
|  | break; | 
|  |  | 
|  | case Tcreate: | 
|  | if (p + BIT32SZ > ep) | 
|  | return 0; | 
|  | f->fid = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | p = gstring(p, ep, &f->name); | 
|  | if (p == NULL) | 
|  | break; | 
|  | if (p + BIT32SZ + BIT8SZ > ep) | 
|  | return 0; | 
|  | f->perm = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | f->mode = GBIT8(p); | 
|  | p += BIT8SZ; | 
|  | break; | 
|  |  | 
|  | case Tread: | 
|  | if (p + BIT32SZ + BIT64SZ + BIT32SZ > ep) | 
|  | return 0; | 
|  | f->fid = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | f->offset = GBIT64(p); | 
|  | p += BIT64SZ; | 
|  | f->count = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | break; | 
|  |  | 
|  | case Twrite: | 
|  | if (p + BIT32SZ + BIT64SZ + BIT32SZ > ep) | 
|  | return 0; | 
|  | f->fid = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | f->offset = GBIT64(p); | 
|  | p += BIT64SZ; | 
|  | f->count = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | if (p + f->count > ep) | 
|  | return 0; | 
|  | f->data = (char *)p; | 
|  | p += f->count; | 
|  | break; | 
|  |  | 
|  | case Tclunk: | 
|  | case Tremove: | 
|  | if (p + BIT32SZ > ep) | 
|  | return 0; | 
|  | f->fid = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | break; | 
|  |  | 
|  | case Tstat: | 
|  | if (p + BIT32SZ > ep) | 
|  | return 0; | 
|  | f->fid = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | break; | 
|  |  | 
|  | case Twstat: | 
|  | if (p + BIT32SZ + BIT16SZ > ep) | 
|  | return 0; | 
|  | f->fid = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | f->nstat = GBIT16(p); | 
|  | p += BIT16SZ; | 
|  | if (p + f->nstat > ep) | 
|  | return 0; | 
|  | f->stat = p; | 
|  | p += f->nstat; | 
|  | break; | 
|  |  | 
|  |  | 
|  |  | 
|  | case Rversion: | 
|  | if (p + BIT32SZ > ep) | 
|  | return 0; | 
|  | f->msize = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | p = gstring(p, ep, &f->version); | 
|  | break; | 
|  |  | 
|  | case Rerror: | 
|  | p = gstring(p, ep, &f->ename); | 
|  | break; | 
|  |  | 
|  | case Rflush: | 
|  | break; | 
|  |  | 
|  | case Rauth: | 
|  | p = gqid(p, ep, &f->aqid); | 
|  | if (p == NULL) | 
|  | break; | 
|  | break; | 
|  |  | 
|  | case Rattach: | 
|  | p = gqid(p, ep, &f->qid); | 
|  | if (p == NULL) | 
|  | break; | 
|  | break; | 
|  |  | 
|  | case Rwalk: | 
|  | if (p + BIT16SZ > ep) | 
|  | return 0; | 
|  | f->nwqid = GBIT16(p); | 
|  | p += BIT16SZ; | 
|  | if (f->nwqid > MAXWELEM) | 
|  | return 0; | 
|  | for (i = 0; i < f->nwqid; i++) { | 
|  | p = gqid(p, ep, &f->wqid[i]); | 
|  | if (p == NULL) | 
|  | break; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case Ropen: | 
|  | case Rcreate: | 
|  | p = gqid(p, ep, &f->qid); | 
|  | if (p == NULL) | 
|  | break; | 
|  | if (p + BIT32SZ > ep) | 
|  | return 0; | 
|  | f->iounit = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | break; | 
|  |  | 
|  | case Rread: | 
|  | if (p + BIT32SZ > ep) | 
|  | return 0; | 
|  | f->count = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | if (p + f->count > ep) | 
|  | return 0; | 
|  | f->data = (char *)p; | 
|  | p += f->count; | 
|  | break; | 
|  |  | 
|  | case Rwrite: | 
|  | if (p + BIT32SZ > ep) | 
|  | return 0; | 
|  | f->count = GBIT32(p); | 
|  | p += BIT32SZ; | 
|  | break; | 
|  |  | 
|  | case Rclunk: | 
|  | case Rremove: | 
|  | break; | 
|  |  | 
|  | case Rstat: | 
|  | if (p + BIT16SZ > ep) | 
|  | return 0; | 
|  | f->nstat = GBIT16(p); | 
|  | p += BIT16SZ; | 
|  | if (p + f->nstat > ep) | 
|  | return 0; | 
|  | f->stat = p; | 
|  | p += f->nstat; | 
|  | break; | 
|  |  | 
|  | case Rwstat: | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (p == NULL || p > ep) | 
|  | return 0; | 
|  | if (ap + size == p) | 
|  | return size; | 
|  | return 0; | 
|  | } |