Fix regexp object (scope) vs GC deadlock (76233, r=beard, sr=shaver, a=asa).

This commit is contained in:
brendan%mozilla.org 2001-04-19 21:15:40 +00:00
Родитель 09cab04692
Коммит 28427e3770
2 изменённых файлов: 79 добавлений и 76 удалений

Просмотреть файл

@ -1222,9 +1222,10 @@ js_NewRegExp(JSContext *cx, JSTokenStream *ts,
#endif #endif
resize = sizeof *re + state.progLength - 1; resize = sizeof *re + state.progLength - 1;
re = (JSRegExp*) JS_malloc(cx, JS_ROUNDUP(resize, sizeof(jsword))); re = (JSRegExp *) JS_malloc(cx, JS_ROUNDUP(resize, sizeof(jsword)));
if (!re) if (!re)
goto out; goto out;
re->nrefs = 1;
re->source = str; re->source = str;
re->lastIndex = 0; re->lastIndex = 0;
re->parenCount = state.parenCount; re->parenCount = state.parenCount;
@ -1305,12 +1306,17 @@ static void freeRENtree(JSContext *cx, RENode *ren, RENode *stop)
} }
} }
#define HOLD_REGEXP(cx, re) JS_ATOMIC_INCREMENT(&(re)->nrefs)
#define DROP_REGEXP(cx, re) js_DestroyRegExp(cx, re)
void void
js_DestroyRegExp(JSContext *cx, JSRegExp *re) js_DestroyRegExp(JSContext *cx, JSRegExp *re)
{ {
js_UnlockGCThing(cx, re->source); if (JS_ATOMIC_DECREMENT(&re->nrefs) == 0) {
freeRENtree(cx, re->ren, NULL); js_UnlockGCThing(cx, re->source);
JS_free(cx, re); freeRENtree(cx, re->ren, NULL);
JS_free(cx, re);
}
} }
typedef struct MatchState { typedef struct MatchState {
@ -2283,7 +2289,7 @@ regexp_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
return JS_TRUE; return JS_TRUE;
slot = JSVAL_TO_INT(id); slot = JSVAL_TO_INT(id);
JS_LOCK_OBJ(cx, obj); JS_LOCK_OBJ(cx, obj);
re = (JSRegExp*) JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL); re = (JSRegExp *) JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL);
if (re) { if (re) {
switch (slot) { switch (slot) {
case REGEXP_SOURCE: case REGEXP_SOURCE:
@ -2296,9 +2302,8 @@ regexp_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
*vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_FOLD) != 0); *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_FOLD) != 0);
break; break;
case REGEXP_LAST_INDEX: case REGEXP_LAST_INDEX:
if (!js_NewNumberValue(cx, (jsdouble)re->lastIndex, vp)) *vp = INT_TO_JSVAL((jsint) re->lastIndex);
return JS_FALSE; break;
break;
case REGEXP_MULTILINE: case REGEXP_MULTILINE:
*vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_MULTILINE) != 0); *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_MULTILINE) != 0);
break; break;
@ -2318,14 +2323,16 @@ regexp_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
if (!JSVAL_IS_INT(id)) if (!JSVAL_IS_INT(id))
return JS_TRUE; return JS_TRUE;
slot = JSVAL_TO_INT(id); slot = JSVAL_TO_INT(id);
JS_LOCK_OBJ(cx, obj); if (slot == REGEXP_LAST_INDEX) {
re = (JSRegExp*) JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL); if (!js_ValueToNumber(cx, *vp, &d))
if (re && slot == REGEXP_LAST_INDEX) { return JS_FALSE;
if (!js_ValueToNumber(cx, *vp, &d)) d = js_DoubleToInteger(d);
return JS_FALSE; JS_LOCK_OBJ(cx, obj);
re->lastIndex = (uintN)d; re = (JSRegExp *) JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL);
if (re)
re->lastIndex = (uintN)d;
JS_UNLOCK_OBJ(cx, obj);
} }
JS_UNLOCK_OBJ(cx, obj);
return JS_TRUE; return JS_TRUE;
} }
@ -2487,7 +2494,7 @@ regexp_finalize(JSContext *cx, JSObject *obj)
{ {
JSRegExp *re; JSRegExp *re;
re = (JSRegExp*) JS_GetPrivate(cx, obj); re = (JSRegExp *) JS_GetPrivate(cx, obj);
if (!re) if (!re)
return; return;
js_DestroyRegExp(cx, re); js_DestroyRegExp(cx, re);
@ -2516,7 +2523,7 @@ regexp_xdrObject(JSXDRState *xdr, JSObject **objp)
uint8 flags; uint8 flags;
if (xdr->mode == JSXDR_ENCODE) { if (xdr->mode == JSXDR_ENCODE) {
re = (JSRegExp*) JS_GetPrivate(xdr->cx, *objp); re = (JSRegExp *) JS_GetPrivate(xdr->cx, *objp);
if (!re) if (!re)
return JS_FALSE; return JS_FALSE;
source = re->source; source = re->source;
@ -2560,7 +2567,6 @@ static JSBool
regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval) jsval *rval)
{ {
JSBool ok;
JSRegExp *re; JSRegExp *re;
jschar *chars; jschar *chars;
size_t length, nflags; size_t length, nflags;
@ -2569,12 +2575,12 @@ regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv)) if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv))
return JS_FALSE; return JS_FALSE;
ok = JS_TRUE;
JS_LOCK_OBJ(cx, obj); JS_LOCK_OBJ(cx, obj);
re = (JSRegExp*) JS_GetPrivate(cx, obj); re = (JSRegExp *) JS_GetPrivate(cx, obj);
if (!re) { if (!re) {
JS_UNLOCK_OBJ(cx, obj);
*rval = STRING_TO_JSVAL(cx->runtime->emptyString); *rval = STRING_TO_JSVAL(cx->runtime->emptyString);
goto out; return JS_TRUE;
} }
length = re->source->length + 2; length = re->source->length + 2;
@ -2583,8 +2589,8 @@ regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
nflags++; nflags++;
chars = (jschar*) JS_malloc(cx, (length + nflags + 1) * sizeof(jschar)); chars = (jschar*) JS_malloc(cx, (length + nflags + 1) * sizeof(jschar));
if (!chars) { if (!chars) {
ok = JS_FALSE; JS_UNLOCK_OBJ(cx, obj);
goto out; return JS_FALSE;
} }
chars[0] = '/'; chars[0] = '/';
@ -2598,18 +2604,16 @@ regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
if (re->flags & JSREG_MULTILINE) if (re->flags & JSREG_MULTILINE)
chars[length++] = 'm'; chars[length++] = 'm';
} }
JS_UNLOCK_OBJ(cx, obj);
chars[length] = 0; chars[length] = 0;
str = js_NewString(cx, chars, length, 0); str = js_NewString(cx, chars, length, 0);
if (!str) { if (!str) {
JS_free(cx, chars); JS_free(cx, chars);
ok = JS_FALSE; return JS_FALSE;
goto out;
} }
*rval = STRING_TO_JSVAL(str); *rval = STRING_TO_JSVAL(str);
out: return JS_TRUE;
JS_UNLOCK_OBJ(cx, obj);
return ok;
} }
static JSBool static JSBool
@ -2624,17 +2628,13 @@ regexp_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv)) if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv))
return JS_FALSE; return JS_FALSE;
opt = NULL; opt = NULL;
JS_LOCK_OBJ(cx, obj);
if (argc == 0) { if (argc == 0) {
str = js_NewStringCopyN(cx, cx->runtime->emptyString->chars, str = js_NewStringCopyN(cx, cx->runtime->emptyString->chars,
cx->runtime->emptyString->length, 0); cx->runtime->emptyString->length, 0);
if (!str) { if (!str)
ok = JS_FALSE; return JS_FALSE;
goto out;
}
} else { } else {
if (JSVAL_IS_OBJECT(argv[0])) { if (JSVAL_IS_OBJECT(argv[0])) {
obj2 = JSVAL_TO_OBJECT(argv[0]);
/* /*
* If we get passed in a RegExp object we construct a new * If we get passed in a RegExp object we construct a new
* RegExp that is a duplicate of it by re-compiling the * RegExp that is a duplicate of it by re-compiling the
@ -2642,58 +2642,54 @@ regexp_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
* here if the flags are specified. (We must use the flags * here if the flags are specified. (We must use the flags
* from the original RegExp also). * from the original RegExp also).
*/ */
obj2 = JSVAL_TO_OBJECT(argv[0]);
if (obj2 && OBJ_GET_CLASS(cx, obj2) == &js_RegExpClass) { if (obj2 && OBJ_GET_CLASS(cx, obj2) == &js_RegExpClass) {
if (argc >= 2 && !JSVAL_IS_VOID(argv[1])) { /* 'flags' defined */ if (argc >= 2 && !JSVAL_IS_VOID(argv[1])) { /* 'flags' passed */
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_NEWREGEXP_FLAGGED); JSMSG_NEWREGEXP_FLAGGED);
ok = JS_FALSE; return JS_FALSE;
goto out;
} }
re = (JSRegExp*) JS_GetPrivate(cx, obj2); JS_LOCK_OBJ(cx, obj2);
re = (JSRegExp *) JS_GetPrivate(cx, obj2);
if (!re) { if (!re) {
ok = JS_FALSE; JS_UNLOCK_OBJ(cx, obj2);
goto out; return JS_FALSE;
} }
re = js_NewRegExp(cx, NULL, re->source, re->flags, JS_FALSE); re = js_NewRegExp(cx, NULL, re->source, re->flags, JS_FALSE);
JS_UNLOCK_OBJ(cx, obj2);
goto madeit; goto madeit;
} }
} }
str = js_ValueToString(cx, argv[0]); str = js_ValueToString(cx, argv[0]);
if (!str) { if (!str)
ok = JS_FALSE; return JS_FALSE;
goto out;
}
argv[0] = STRING_TO_JSVAL(str); argv[0] = STRING_TO_JSVAL(str);
if (argc > 1) { if (argc > 1) {
if (JSVAL_IS_VOID(argv[1])) if (JSVAL_IS_VOID(argv[1])) {
opt = NULL; opt = NULL;
else { } else {
opt = js_ValueToString(cx, argv[1]); opt = js_ValueToString(cx, argv[1]);
if (!opt) { if (!opt)
ok = JS_FALSE; return JS_FALSE;
goto out;
}
argv[1] = STRING_TO_JSVAL(opt); argv[1] = STRING_TO_JSVAL(opt);
} }
} }
} }
re = js_NewRegExpOpt(cx, NULL, str, opt, JS_FALSE); re = js_NewRegExpOpt(cx, NULL, str, opt, JS_FALSE);
madeit: madeit:
if (!re) { if (!re)
ok = JS_FALSE; return JS_FALSE;
goto out; JS_LOCK_OBJ(cx, obj);
} oldre = (JSRegExp *) JS_GetPrivate(cx, obj);
oldre = (JSRegExp*) JS_GetPrivate(cx, obj);
ok = JS_SetPrivate(cx, obj, re); ok = JS_SetPrivate(cx, obj, re);
JS_UNLOCK_OBJ(cx, obj);
if (!ok) { if (!ok) {
js_DestroyRegExp(cx, re); js_DestroyRegExp(cx, re);
goto out; } else {
if (oldre)
js_DestroyRegExp(cx, oldre);
*rval = OBJECT_TO_JSVAL(obj);
} }
if (oldre)
js_DestroyRegExp(cx, oldre);
*rval = OBJECT_TO_JSVAL(obj);
out:
JS_UNLOCK_OBJ(cx, obj);
return ok; return ok;
} }
@ -2701,17 +2697,27 @@ static JSBool
regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
JSBool test, jsval *rval) JSBool test, jsval *rval)
{ {
JSBool ok, locked; JSBool ok;
JSRegExp *re; JSRegExp *re;
JSString *str; JSString *str;
size_t i; size_t i;
ok = JS_FALSE;
if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv)) if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv))
return JS_FALSE; return ok;
re = (JSRegExp*) JS_GetPrivate(cx, obj); JS_LOCK_OBJ(cx, obj);
if (!re) re = (JSRegExp *) JS_GetPrivate(cx, obj);
if (!re) {
JS_UNLOCK_OBJ(cx, obj);
return JS_TRUE; return JS_TRUE;
ok = locked = JS_FALSE; }
/* NB: we must reach out: after this paragraph, in order to drop re. */
HOLD_REGEXP(cx, re);
i = (re->flags & JSREG_GLOB) ? re->lastIndex : 0;
JS_UNLOCK_OBJ(cx, obj);
/* Now that obj is unlocked, it's safe to (potentially) grab the GC lock. */
if (argc == 0) { if (argc == 0) {
str = cx->regExpStatics.input; str = cx->regExpStatics.input;
if (!str) { if (!str) {
@ -2721,7 +2727,7 @@ regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
(re->flags & JSREG_GLOB) ? "g" : "", (re->flags & JSREG_GLOB) ? "g" : "",
(re->flags & JSREG_FOLD) ? "i" : "", (re->flags & JSREG_FOLD) ? "i" : "",
(re->flags & JSREG_MULTILINE) ? "m" : ""); (re->flags & JSREG_MULTILINE) ? "m" : "");
goto out; goto out;
} }
} else { } else {
str = js_ValueToString(cx, argv[0]); str = js_ValueToString(cx, argv[0]);
@ -2729,19 +2735,15 @@ regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
goto out; goto out;
argv[0] = STRING_TO_JSVAL(str); argv[0] = STRING_TO_JSVAL(str);
} }
if (re->flags & JSREG_GLOB) {
JS_LOCK_OBJ(cx, obj);
locked = JS_TRUE;
i = re->lastIndex;
} else {
i = 0;
}
ok = js_ExecuteRegExp(cx, re, str, &i, test, rval); ok = js_ExecuteRegExp(cx, re, str, &i, test, rval);
JS_LOCK_OBJ(cx, obj);
if (re->flags & JSREG_GLOB) if (re->flags & JSREG_GLOB)
re->lastIndex = (*rval == JSVAL_NULL) ? 0 : i; re->lastIndex = (*rval == JSVAL_NULL) ? 0 : i;
JS_UNLOCK_OBJ(cx, obj);
out: out:
if (locked) DROP_REGEXP(cx, re);
JS_UNLOCK_OBJ(cx, obj);
return ok; return ok;
} }

Просмотреть файл

@ -66,6 +66,7 @@ struct JSRegExpStatics {
: &js_EmptySubString) : &js_EmptySubString)
struct JSRegExp { struct JSRegExp {
jsrefcount nrefs; /* reference count */
JSString *source; /* locked source string, sans // */ JSString *source; /* locked source string, sans // */
uintN lastIndex; /* index after last match, for //g iterator */ uintN lastIndex; /* index after last match, for //g iterator */
uintN parenCount; /* number of parenthesized submatches */ uintN parenCount; /* number of parenthesized submatches */