lib9/fmt: avoid racy access to installed fmt formats

This commit is contained in:
Russ Cox 2020-01-13 11:41:37 -05:00
parent d96e9e5dc3
commit 7ba9f9467d
4 changed files with 55 additions and 22 deletions

View file

@ -72,7 +72,7 @@ static Convfmt knownfmt[] = {
int (*fmtdoquote)(int); int (*fmtdoquote)(int);
/* /*
* __fmtlock() must be set * __fmtwlock() must be set
*/ */
static int static int
__fmtinstall(int c, Fmts f) __fmtinstall(int c, Fmts f)
@ -106,34 +106,43 @@ fmtinstall(int c, int (*f)(Fmt*))
{ {
int ret; int ret;
__fmtlock(); __fmtwlock();
ret = __fmtinstall(c, f); ret = __fmtinstall(c, f);
__fmtunlock(); __fmtwunlock();
return ret; return ret;
} }
static Fmts static Fmts
fmtfmt(int c) fmtfmt(int c)
{ {
Convfmt *p, *ep; Convfmt *p, *ep, *kp;
/* conflict-free check - common case */
__fmtrlock();
ep = &fmtalloc.fmt[fmtalloc.nfmt]; ep = &fmtalloc.fmt[fmtalloc.nfmt];
for(p=fmtalloc.fmt; p<ep; p++) for(p=fmtalloc.fmt; p<ep; p++)
if(p->c == c){ if(p->c == c){
while(p->fmt == nil) /* loop until value is updated */ __fmtrunlock();
;
return p->fmt; return p->fmt;
} }
__fmtrunlock();
/* is this a predefined format char? */ /* is this a predefined format char? */
__fmtlock(); for(kp=knownfmt; kp->c; kp++){
for(p=knownfmt; p->c; p++) if(kp->c == c){
__fmtwlock();
/* double-check fmtinstall didn't happen */
for(p=fmtalloc.fmt; p<ep; p++){
if(p->c == c){ if(p->c == c){
__fmtinstall(p->c, p->fmt); __fmtwunlock();
__fmtunlock();
return p->fmt; return p->fmt;
} }
__fmtunlock(); }
__fmtinstall(kp->c, kp->fmt);
__fmtwunlock();
return kp->fmt;
}
}
return __badfmt; return __badfmt;
} }

View file

@ -33,11 +33,13 @@ int __fmtFdFlush(Fmt *f);
int __fmtcpy(Fmt *f, const void *vm, int n, int sz); int __fmtcpy(Fmt *f, const void *vm, int n, int sz);
void* __fmtdispatch(Fmt *f, void *fmt, int isrunes); void* __fmtdispatch(Fmt *f, void *fmt, int isrunes);
void * __fmtflush(Fmt *f, void *t, int len); void * __fmtflush(Fmt *f, void *t, int len);
void __fmtlock(void);
int __fmtpad(Fmt *f, int n); int __fmtpad(Fmt *f, int n);
double __fmtpow10(int n); double __fmtpow10(int n);
int __fmtrcpy(Fmt *f, const void *vm, int n); int __fmtrcpy(Fmt *f, const void *vm, int n);
void __fmtunlock(void); void __fmtrlock(void);
void __fmtrunlock(void);
void __fmtwlock(void);
void __fmtwunlock(void);
int __ifmt(Fmt *f); int __ifmt(Fmt *f);
int __isInf(double d, int sign); int __isInf(double d, int sign);
int __isNaN(double d); int __isNaN(double d);

View file

@ -5,11 +5,21 @@
#include "fmtdef.h" #include "fmtdef.h"
void void
__fmtlock(void) __fmtrlock(void)
{ {
} }
void void
__fmtunlock(void) __fmtrunlock(void)
{
}
void
__fmtwlock(void)
{
}
void
__fmtwunlock(void)
{ {
} }

View file

@ -1,16 +1,28 @@
#include <u.h> #include <u.h>
#include <libc.h> #include <libc.h>
static Lock fmtlock; static RWLock fmtlock;
void void
__fmtlock(void) __fmtrlock(void)
{ {
lock(&fmtlock); rlock(&fmtlock);
} }
void void
__fmtunlock(void) __fmtrunlock(void)
{ {
unlock(&fmtlock); runlock(&fmtlock);
}
void
__fmtwlock(void)
{
wlock(&fmtlock);
}
void
__fmtwunlock(void)
{
wunlock(&fmtlock);
} }