ref: 6aee114052c8ccef5bf9685aca8015979846e712
parent: cf55fc8d7bffe4158e1db92bd3423fd89b47b1cc
author: cinap_lenrek <cinap_lenrek@gmx.de>
date: Fri Mar 23 00:02:34 EDT 2012
ndb/cs: fix use after free caused by flush/clunk happening before dns lookup finishes
--- a/sys/include/ndb.h
+++ b/sys/include/ndb.h
@@ -31,7 +31,7 @@
ulong mtime; /* mtime of db file */
Qid qid; /* qid of db file */
- char file[128];/* path name of db file */
+ char file[128]; /* path name of db file */
ulong length; /* length of db file */
int nohash; /* don't look for hash files */
--- a/sys/src/cmd/ndb/cs.c
+++ b/sys/src/cmd/ndb/cs.c
@@ -32,7 +32,8 @@
struct Mfile
{- int busy;
+ int busy; /* fid in use */
+ int ref; /* cleanup when drops to zero */
char *user;
Qid qid;
@@ -72,7 +73,7 @@
Fcall request;
Fcall reply;
};
-Lock joblock;
+QLock joblock;
Job *joblist;
Mlist *mlist;
@@ -121,8 +122,8 @@
extern void paralloc(void);
-Lock dblock; /* mutex on database operations */
-Lock netlock; /* mutex for netinit() */
+QLock dblock; /* mutex on database operations */
+QLock netlock; /* mutex for netinit() */
char *logfile = "cs";
char *paranoiafile = "cs.paranoia";
@@ -167,7 +168,7 @@
{ 0 },};
-Lock ipifclock;
+QLock ipifclock;
Ipifc *ipifcs;
char eaddr[16]; /* ascii ethernet address */
@@ -357,7 +358,7 @@
for(f = mlist; f; f = f->next)
if(f->mf.busy && f->mf.fid == fid)
return &f->mf;
- else if(!ff && !f->mf.busy)
+ else if(!ff && !f->mf.busy && !f->mf.ref)
ff = f;
if(ff == 0){ff = emalloc(sizeof *f);
@@ -376,11 +377,11 @@
Job *job;
job = mallocz(sizeof(Job), 1);
- lock(&joblock);
+ qlock(&joblock);
job->next = joblist;
joblist = job;
job->request.tag = -1;
- unlock(&joblock);
+ qunlock(&joblock);
return job;
}
@@ -389,15 +390,15 @@
{Job **l;
- lock(&joblock);
+ qlock(&joblock);
for(l = &joblist; *l; l = &(*l)->next){ if((*l) == job){*l = job->next;
- free(job);
break;
}
}
- unlock(&joblock);
+ qunlock(&joblock);
+ free(job);
}
void
@@ -405,7 +406,7 @@
{Job *job;
- lock(&joblock);
+ qlock(&joblock);
for(job = joblist; job; job = job->next){ if(job->request.tag == tag && job->request.type != Tflush){job->flushed = 1;
@@ -412,7 +413,7 @@
break;
}
}
- unlock(&joblock);
+ qunlock(&joblock);
}
void
@@ -448,7 +449,7 @@
freejob(job);
continue;
}
- lock(&dblock);
+ qlock(&dblock);
mf = newfid(job->request.fid);
if(debug)
syslog(0, logfile, "%F", &job->request);
@@ -498,7 +499,7 @@
rwstat(job, mf);
break;
}
- unlock(&dblock);
+ qunlock(&dblock);
freejob(job);
@@ -668,6 +669,8 @@
err = 0;
off = job->request.offset;
cnt = job->request.count;
+ mf->ref++;
+
if(mf->qid.type & QTDIR){clock = time(0);
if(off == 0){@@ -686,38 +689,44 @@
n = convD2M(&dir, buf, sizeof buf);
}
job->reply.data = (char*)buf;
- } else {- for(;;){- /* look for an answer at the right offset */
- toff = 0;
- for(i = 0; mf->reply[i] && i < mf->nreply; i++){- n = mf->replylen[i];
- if(off < toff + n)
- break;
- toff += n;
- }
- if(i < mf->nreply)
- break; /* got something to return */
+ goto send;
+ }
- /* try looking up more answers */
- if(lookup(mf) == 0){- /* no more */
- n = 0;
- goto send;
- }
+ for(;;){+ /* look for an answer at the right offset */
+ toff = 0;
+ for(i = 0; mf->reply[i] && i < mf->nreply; i++){+ n = mf->replylen[i];
+ if(off < toff + n)
+ break;
+ toff += n;
}
+ if(i < mf->nreply)
+ break; /* got something to return */
- /* give back a single reply (or part of one) */
- job->reply.data = mf->reply[i] + (off - toff);
- if(cnt > toff - off + n)
- n = toff - off + n;
- else
- n = cnt;
+ /* try looking up more answers */
+ if(lookup(mf) == 0 || job->flushed){+ /* no more */
+ n = 0;
+ goto send;
+ }
}
+
+ /* give back a single reply (or part of one) */
+ job->reply.data = mf->reply[i] + (off - toff);
+ if(cnt > toff - off + n)
+ n = toff - off + n;
+ else
+ n = cnt;
+
send:
job->reply.count = n;
sendmsg(job, err);
+
+ if(--mf->ref == 0 && mf->busy == 0)
+ cleanmf(mf);
}
+
void
cleanmf(Mfile *mf)
{@@ -814,6 +823,12 @@
goto send;
}
+ if(mf->ref){+ err = "query already in progress";
+ goto send;
+ }
+ mf->ref++;
+
/* start transaction with a clean slate */
cleanmf(mf);
@@ -822,7 +837,7 @@
*/
if(*job->request.data == '!'){err = genquery(mf, job->request.data+1);
- goto send;
+ goto done;
}
if(debug)
@@ -829,7 +844,6 @@
syslog(0, logfile, "write %s", job->request.data);
if(paranoia)
syslog(0, paranoiafile, "write %s by %s", job->request.data, mf->user);
-
/*
* break up name
*/
@@ -858,6 +872,11 @@
rerrstr(curerr, sizeof curerr);
err = curerr;
}
+
+done:
+ if(--mf->ref == 0 && mf->busy == 0)
+ cleanmf(mf);
+
send:
job->reply.count = cnt;
sendmsg(job, err);
@@ -866,11 +885,12 @@
void
rclunk(Job *job, Mfile *mf)
{- cleanmf(mf);
+ if(mf->ref == 0)
+ cleanmf(mf);
free(mf->user);
mf->user = 0;
- mf->busy = 0;
mf->fid = 0;
+ mf->busy = 0;
sendmsg(job, 0);
}
@@ -933,11 +953,11 @@
syslog(1, logfile, "sendmsg convS2M of %F returns 0", &job->reply);
abort();
}
- lock(&joblock);
+ qlock(&joblock);
if(job->flushed == 0)
if(write(mfd[1], mdata, n)!=n)
error("mount write");- unlock(&joblock);
+ qunlock(&joblock);
if(debug)
syslog(0, logfile, "%F %d", &job->reply, n);
}
@@ -1082,7 +1102,7 @@
default:
return;
}
- lock(&netlock);
+ qlock(&netlock);
}
/* add the mounted networks to the default list */
@@ -1112,7 +1132,7 @@
mysysname?mysysname:"???", eaddr, ipaddr, ipa);
if(background){- unlock(&netlock);
+ qunlock(&netlock);
_exits(0);
}
}
@@ -1276,7 +1296,7 @@
/* '*' means any service */
if(strcmp(name, "*")==0){- strcpy(buf, name);
+ nstrcpy(buf, name, blen);
return buf;
}
@@ -1384,6 +1404,7 @@
if(strcmp("::", host) == 0) return ndbnew("ip", "*");+
/*
* '$' means the rest of the name is an attribute that we
* need to search for
@@ -1396,9 +1417,15 @@
/*
* turn '[ip address]' into just 'ip address'
*/
- if(*host == '[' && host[strlen(host)-1] == ']'){- host++;
- host[strlen(host)-1] = 0;
+ if(*host == '['){+ char *x;
+
+ if(host != dollar){+ nstrcpy(dollar, host, sizeof dollar);
+ host = dollar;
+ }
+ if(x = strchr(++host, ']'))
+ *x = 0;
}
/*
@@ -1439,7 +1466,7 @@
/*
* reorder according to our interfaces
*/
- lock(&ipifclock);
+ qlock(&ipifclock);
for(ifc = ipifcs; ifc != nil; ifc = ifc->next){ for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){maskip(lifc->ip, lifc->mask, net);
@@ -1450,13 +1477,13 @@
maskip(ip, lifc->mask, tnet);
if(memcmp(net, tnet, IPaddrlen) == 0){t = reorder(t, nt);
- unlock(&ipifclock);
+ qunlock(&ipifclock);
return t;
}
}
}
}
- unlock(&ipifclock);
+ qunlock(&ipifclock);
return t;
}
@@ -1602,7 +1629,7 @@
/* convert ipv6 attr to ip */
for (tt = t6; tt != nil; tt = tt->entry)
if (strcmp(tt->attr, "ipv6") == 0)
- strncpy(tt->attr, "ip", sizeof tt->attr - 1);
+ strcpy(tt->attr, "ip");
if (t == nil)
return t6;
@@ -1623,20 +1650,16 @@
char buf[Maxreply];
Ndbtuple *t;
- unlock(&dblock);
-
- /* save the name before starting a slave */
- snprint(buf, sizeof(buf), "%s", host);
-
+ qunlock(&dblock);
slave(host);
- if(strcmp(ipattr(buf), "ip") == 0)
- t = dnsquery(mntpt, buf, "ptr");
+ if(strcmp(ipattr(host), "ip") == 0)
+ t = dnsquery(mntpt, host, "ptr");
else {- t = dnsquery(mntpt, buf, "ip");
+ t = dnsquery(mntpt, host, "ip");
/* special case: query ipv6 (AAAA dns RR) too */
if (ipv6lookups)
- t = dnsip6lookup(mntpt, buf, t);
+ t = dnsip6lookup(mntpt, host, t);
}
s->t = t;
@@ -1648,7 +1671,7 @@
werrstr("temporary problem: %s", buf);}
- lock(&dblock);
+ qlock(&dblock);
return t;
}
@@ -1787,7 +1810,7 @@
ndbfree(nt);
continue;
}
- strcpy(nt->attr, attr);
+ nstrcpy(nt->attr, attr, sizeof(nt->attr));
l = &nt->entry;
}
return t;
--- a/sys/src/cmd/ndb/dns.c
+++ b/sys/src/cmd/ndb/dns.c
@@ -33,7 +33,6 @@
struct Mfile
{Mfile *next; /* next free mfile */
- int ref;
char *user;
Qid qid;
@@ -339,8 +338,7 @@
for(l = &mfalloc.inuse; *l != nil; l = &(*l)->next)
if(*l == mf){*l = mf->next;
- if(mf->user)
- free(mf->user);
+ free(mf->user);
memset(mf, 0, sizeof *mf); /* cause trouble */
free(mf);
unlock(&mfalloc);
--- a/sys/src/libndb/dnsquery.c
+++ b/sys/src/libndb/dnsquery.c
@@ -81,12 +81,17 @@
char *p, *np;
int len;
- if(strstr(ip, "in-addr.arpa") || strstr(ip, "IN-ADDR.ARPA")){+ if(cistrstr(ip, "in-addr.arpa")){nstrcpy(rip, ip, rlen);
return;
}
-
nstrcpy(buf, ip, sizeof buf);
+
+ /* truncate if result wont fit in rip[rlen] */
+ assert(rlen > 14);
+ if((rlen-14) < sizeof(buf))
+ buf[rlen-14] = 0;
+
for(p = buf; *p; p++)
;
*p = '.';
--
⑨