ref: f763dc164092f5a4652c65feab358130857f0622
parent: af23bb343a067d5346696b788f4cce5b744cd51d
author: cinap_lenrek <[email protected]>
date: Mon Oct 7 07:52:14 EDT 2019
sshfs: fix race condition between sendproc() and recvproc() there was a race between the sendproc putting the request on the sreqrd[] id and the recvproc handling the response, and potentially freeing the request before the sendproc() was finished with the request (or the fid). so we defer allocating a request id and putting it on the sreqrd[] stage after we have completely generated the request in vpack(). this prevents the handling of the request before it is even sent. this also means that the SReq should not be touched after calling sendpkt(), submitreq(), submitsreq(). secondly, putsfid() needs to acquire the RWLock to make sure sendproc() is finished with the request. the scenario is that recvproc() can call respond() on the request before sendproc() has unlocked the SFid.
--- a/sys/src/cmd/sshfs.c
+++ b/sys/src/cmd/sshfs.c
@@ -103,7 +103,6 @@
struct SReq {
Req *req;
SFid *closefid;
- int reqid;
SReq *next;
};
@@ -194,9 +193,29 @@
}
int
+allocsreqid(SReq *r)
+{
+ int i;
+
+ qlock(&sreqidlock);
+ for(;;){
+ for(i = 0; i < MAXREQID; i++)
+ if(sreqrd[i] == nil){
+ sreqrd[i] = r;
+ goto out;
+ }
+ rsleep(&sreqidrend);
+ }
+out:
+ qunlock(&sreqidlock);
+ return i;
+}
+
+int
vpack(uchar *p, int n, char *fmt, va_list a)
{
uchar *p0 = p, *e = p+n;
+ SReq *sr = nil;
u32int u;
u64int v;
void *s;
@@ -205,6 +224,10 @@
for(;;){
switch(c = *fmt++){
case '\0':
+ if(sr != nil){
+ u = allocsreqid(sr);
+ PUT4(p0+1, u);
+ }
return p - p0;
case '_':
if(++p > e) goto err;
@@ -228,6 +251,11 @@
memmove(p, s, u);
p += u;
break;
+ case 'q':
+ p += 4;
+ if(p != p0+5 || p > e) goto err;
+ sr = va_arg(a, SReq*);
+ break;
case 'u':
u = va_arg(a, int);
if(p+4 > e) goto err;
@@ -389,11 +417,14 @@
s->dirpos = 0;
}
-
void
putsfid(SFid *s)
{
if(s == nil) return;
+
+ wlock(s);
+ wunlock(s);
+
free(s->fn);
free(s->hand);
freedir(s);
@@ -403,14 +434,6 @@
void
putsreq(SReq *s)
{
- if(s == nil) return;
- if(s->reqid != -1){
- qlock(&sreqidlock);
- sreqrd[s->reqid] = nil;
- rwakeup(&sreqidrend);
- qunlock(&sreqidlock);
- }
- putsfid(s->closefid);
free(s);
}
@@ -424,7 +447,6 @@
qunlock(&sreqwrlock);
}
-
void
submitreq(Req *r)
{
@@ -431,7 +453,6 @@
SReq *s;
s = emalloc9p(sizeof(SReq));
- s->reqid = -1;
s->req = r;
submitsreq(s);
}
@@ -697,20 +718,18 @@
void
sshfsread(Req *r)
{
- SFid *sf;
-
if((r->fid->qid.type & QTDIR) == 0){
submitreq(r);
return;
}
- sf = r->fid->aux;
if(r->ifcall.offset == 0){
+ SFid *sf = r->fid->aux;
wlock(sf);
freedir(sf);
if(sf->dirreads > 0){
+ wunlock(sf);
r->aux = (void*)-1;
submitreq(r);
- wunlock(sf);
return;
}
wunlock(sf);
@@ -764,7 +783,6 @@
{
SReq *r;
SFid *sf;
- int i;
int x, y;
char *s, *t;
@@ -778,41 +796,33 @@
sreqwr = r->next;
if(sreqwr == nil) sreqlast = &sreqwr;
qunlock(&sreqwrlock);
-
- qlock(&sreqidlock);
- idagain:
- for(i = 0; i < MAXREQID; i++)
- if(sreqrd[i] == nil){
- sreqrd[i] = r;
- r->reqid = i;
- break;
- }
- if(i == MAXREQID){
- rsleep(&sreqidrend);
- goto idagain;
- }
- qunlock(&sreqidlock);
- if(r->closefid != nil){
- sendpkt("bus", SSH_FXP_CLOSE, r->reqid, r->closefid->hand, r->closefid->handn);
+ sf = r->closefid;
+ if(sf != nil){
+ rlock(sf);
+ sendpkt("bqs", SSH_FXP_CLOSE, r, sf->hand, sf->handn);
+ runlock(sf);
continue;
}
if(r->req == nil)
sysfatal("nil request in queue");
- sf = r->req->fid != nil ? r->req->fid->aux : nil;
+ sf = r->req->fid->aux;
switch(r->req->ifcall.type){
case Tattach:
- sendpkt("bus", SSH_FXP_STAT, r->reqid, sf->fn, strlen(sf->fn));
+ rlock(sf);
+ sendpkt("bqs", SSH_FXP_STAT, r, sf->fn, strlen(sf->fn));
+ runlock(sf);
break;
case Twalk:
- sendpkt("bus", SSH_FXP_STAT, r->reqid, r->req->aux, strlen(r->req->aux));
+ sendpkt("bqs", SSH_FXP_STAT, r, r->req->aux, strlen(r->req->aux));
break;
case Topen:
- rlock(sf);
- if((r->req->ofcall.qid.type & QTDIR) != 0)
- sendpkt("bus", SSH_FXP_OPENDIR, r->reqid, sf->fn, strlen(sf->fn));
- else{
+ if((r->req->ofcall.qid.type & QTDIR) != 0){
+ rlock(sf);
+ sendpkt("bqs", SSH_FXP_OPENDIR, r, sf->fn, strlen(sf->fn));
+ runlock(sf);
+ }else{
x = r->req->ifcall.mode;
y = 0;
switch(x & 3){
@@ -822,15 +832,15 @@
}
if(readonly && (y & SSH_FXF_WRITE) != 0){
respond(r->req, "mounted read-only");
- runlock(sf);
putsreq(r);
break;
}
if((x & OTRUNC) != 0)
y |= SSH_FXF_TRUNC;
- sendpkt("busuu", SSH_FXP_OPEN, r->reqid, sf->fn, strlen(sf->fn), y, 0);
+ rlock(sf);
+ sendpkt("bqsuu", SSH_FXP_OPEN, r, sf->fn, strlen(sf->fn), y, 0);
+ runlock(sf);
}
- runlock(sf);
break;
case Tcreate:
rlock(sf);
@@ -838,12 +848,12 @@
runlock(sf);
if((r->req->ifcall.perm & DMDIR) != 0){
if(r->req->aux == nil){
- sendpkt("busuu", SSH_FXP_MKDIR, r->reqid, s, strlen(s),
- SSH_FILEXFER_ATTR_PERMISSIONS, r->req->ifcall.perm & 0777);
r->req->aux = (void*)-1;
+ sendpkt("bqsuu", SSH_FXP_MKDIR, r, s, strlen(s),
+ SSH_FILEXFER_ATTR_PERMISSIONS, r->req->ifcall.perm & 0777);
}else{
- sendpkt("bus", SSH_FXP_OPENDIR, r->reqid, s, strlen(s));
r->req->aux = (void*)-2;
+ sendpkt("bqs", SSH_FXP_OPENDIR, r, s, strlen(s));
}
free(s);
break;
@@ -855,7 +865,7 @@
case OWRITE: y |= SSH_FXF_WRITE; break;
case ORDWR: y |= SSH_FXF_READ | SSH_FXF_WRITE; break;
}
- sendpkt("busuuu", SSH_FXP_OPEN, r->reqid, s, strlen(s), y,
+ sendpkt("bqsuuu", SSH_FXP_OPEN, r, s, strlen(s), y,
SSH_FILEXFER_ATTR_PERMISSIONS, r->req->ifcall.perm & 0777);
free(s);
break;
@@ -863,7 +873,7 @@
if((r->req->fid->qid.type & QTDIR) != 0){
wlock(sf);
if(r->req->aux == (void*)-1){
- sendpkt("bus", SSH_FXP_CLOSE, r->reqid, sf->hand, sf->handn);
+ sendpkt("bqs", SSH_FXP_CLOSE, r, sf->hand, sf->handn);
free(sf->hand);
sf->hand = nil;
sf->handn = 0;
@@ -870,15 +880,15 @@
sf->direof = 0;
sf->dirreads = 0;
}else if(r->req->aux == (void*)-2){
- sendpkt("bus", SSH_FXP_OPENDIR, r->reqid, sf->fn, strlen(sf->fn));
+ sendpkt("bqs", SSH_FXP_OPENDIR, r, sf->fn, strlen(sf->fn));
}else{
sf->dirreads++;
- sendpkt("bus", SSH_FXP_READDIR, r->reqid, sf->hand, sf->handn);
+ sendpkt("bqs", SSH_FXP_READDIR, r, sf->hand, sf->handn);
}
wunlock(sf);
}else{
rlock(sf);
- sendpkt("busvuu", SSH_FXP_READ, r->reqid, sf->hand, sf->handn,
+ sendpkt("bqsvuu", SSH_FXP_READ, r, sf->hand, sf->handn,
r->req->ifcall.offset, r->req->ifcall.count);
runlock(sf);
}
@@ -886,13 +896,13 @@
case Twrite:
x = r->req->ifcall.count - r->req->ofcall.count;
if(x >= MAXWRITE) x = MAXWRITE;
+ r->req->ofcall.offset = x;
rlock(sf);
- sendpkt("busvs", SSH_FXP_WRITE, r->reqid, sf->hand, sf->handn,
+ sendpkt("bqsvs", SSH_FXP_WRITE, r, sf->hand, sf->handn,
r->req->ifcall.offset + r->req->ofcall.count,
r->req->ifcall.data + r->req->ofcall.count,
x);
runlock(sf);
- r->req->ofcall.offset = x;
break;
case Tstat:
rlock(sf);
@@ -899,20 +909,20 @@
r->req->d.name = finalelem(sf->fn);
r->req->d.qid = sf->qid;
if(sf->handn > 0 && (sf->qid.type & QTDIR) == 0)
- sendpkt("bus", SSH_FXP_FSTAT, r->reqid, sf->hand, sf->handn);
+ sendpkt("bqs", SSH_FXP_FSTAT, r, sf->hand, sf->handn);
else
- sendpkt("bus", SSH_FXP_STAT, r->reqid, sf->fn, strlen(sf->fn));
+ sendpkt("bqs", SSH_FXP_STAT, r, sf->fn, strlen(sf->fn));
runlock(sf);
break;
case Twstat:
- if(r->req->aux == (void *) -1){
+ if(r->req->aux == (void*)-1){
rlock(sf);
s = parentdir(sf->fn);
t = pathcat(s, r->req->d.name);
- free(s);
r->req->aux = t;
- sendpkt("buss", SSH_FXP_RENAME, r->reqid, sf->fn, strlen(sf->fn), t, strlen(t));
+ sendpkt("bqss", SSH_FXP_RENAME, r, sf->fn, strlen(sf->fn), t, strlen(t));
runlock(sf);
+ free(s);
break;
}
x = dir2attrib(&r->req->d, (uchar **) &s);
@@ -923,9 +933,9 @@
}
rlock(sf);
if(sf->handn > 0)
- sendpkt("bus[", SSH_FXP_FSETSTAT, r->reqid, sf->hand, sf->handn, s, x);
+ sendpkt("bqs[", SSH_FXP_FSETSTAT, r, sf->hand, sf->handn, s, x);
else
- sendpkt("bus[", SSH_FXP_SETSTAT, r->reqid, sf->fn, strlen(sf->fn), s, x);
+ sendpkt("bqs[", SSH_FXP_SETSTAT, r, sf->fn, strlen(sf->fn), s, x);
runlock(sf);
free(s);
break;
@@ -932,9 +942,9 @@
case Tremove:
rlock(sf);
if((sf->qid.type & QTDIR) != 0)
- sendpkt("bus", SSH_FXP_RMDIR, r->reqid, sf->fn, strlen(sf->fn));
+ sendpkt("bqs", SSH_FXP_RMDIR, r, sf->fn, strlen(sf->fn));
else
- sendpkt("bus", SSH_FXP_REMOVE, r->reqid, sf->fn, strlen(sf->fn));
+ sendpkt("bqs", SSH_FXP_REMOVE, r, sf->fn, strlen(sf->fn));
runlock(sf);
break;
default:
@@ -983,7 +993,6 @@
r = sreqrd[id];
if(r != nil){
sreqrd[id] = nil;
- r->reqid = -1;
rwakeup(&sreqidrend);
}
qunlock(&sreqidlock);
@@ -992,6 +1001,7 @@
continue;
}
if(r->closefid != nil){
+ putsfid(r->closefid);
putsreq(r);
continue;
}
@@ -998,7 +1008,7 @@
if(r->req == nil)
sysfatal("recvproc: r->req == nil");
- sf = r->req->fid != nil ? r->req->fid->aux : nil;
+ sf = r->req->fid->aux;
okresp = rxlen >= 9 && t == SSH_FXP_STATUS && GET4(rxpkt+5) == SSH_FX_OK;
switch(r->req->ifcall.type){
case Tattach:
@@ -1094,7 +1104,7 @@
break;
}
if(r->req->aux == nil){
- r->req->aux = (void *) -1;
+ r->req->aux = (void*)-1;
submitreq(r->req);
}else{
wlock(sf);
@@ -1199,7 +1209,6 @@
return;
if(sf->hand != nil){
sr = emalloc9p(sizeof(SReq));
- sr->reqid = -1;
sr->closefid = sf;
submitsreq(sr);
}else