Bug 1083913: Column spans are restricted to 31-bit, not 24-bit, signed values. r=shu

This also cleans up the column span / offset conversions, and fixes some comments.
This commit is contained in:
Jim Blandy 2014-11-12 14:51:41 -08:00
parent f5e193968c
commit 155ef09737
6 changed files with 61 additions and 42 deletions

@ -466,17 +466,14 @@ UpdateSourceCoordNotes(ExclusiveContext *cx, BytecodeEmitter *bce, uint32_t offs
uint32_t columnIndex = bce->parser->tokenStream.srcCoords.columnIndex(offset);
ptrdiff_t colspan = ptrdiff_t(columnIndex) - ptrdiff_t(bce->current->lastColumn);
if (colspan != 0) {
if (colspan < 0) {
colspan += SN_COLSPAN_DOMAIN;
} else if (colspan >= SN_COLSPAN_DOMAIN / 2) {
// If the column span is so large that we can't store it, then just
// discard this information because column information would most
// likely be useless anyway once the column numbers are ~4000000.
// This has been known to happen with scripts that have been
// minimized and put into all one line.
// If the column span is so large that we can't store it, then just
// discard this information. This can happen with minimized or otherwise
// machine-generated code. Even gigantic column numbers are still
// valuable if you have a source map to relate them to something real;
// but it's better to fail soft here.
if (!SN_REPRESENTABLE_COLSPAN(colspan))
return true;
}
if (NewSrcNote2(cx, bce, SRC_COLSPAN, colspan) < 0)
if (NewSrcNote2(cx, bce, SRC_COLSPAN, SN_COLSPAN_TO_OFFSET(colspan)) < 0)
return false;
bce->current->lastColumn = columnIndex;
}
@ -7294,7 +7291,7 @@ static bool
SetSrcNoteOffset(ExclusiveContext *cx, BytecodeEmitter *bce, unsigned index, unsigned which,
ptrdiff_t offset)
{
if (size_t(offset) > SN_MAX_OFFSET) {
if (!SN_REPRESENTABLE_OFFSET(offset)) {
ReportStatementTooLarge(bce->parser->tokenStream, bce->topStmt);
return false;
}
@ -7311,14 +7308,14 @@ SetSrcNoteOffset(ExclusiveContext *cx, BytecodeEmitter *bce, unsigned index, uns
}
/*
* See if the new offset requires three bytes either by being too big or if
* See if the new offset requires four bytes either by being too big or if
* the offset has already been inflated (in which case, we need to stay big
* to not break the srcnote encoding if this isn't the last srcnote).
*/
if (offset > (ptrdiff_t)SN_4BYTE_OFFSET_MASK || (*sn & SN_4BYTE_OFFSET_FLAG)) {
/* Maybe this offset was already set to a three-byte value. */
/* Maybe this offset was already set to a four-byte value. */
if (!(*sn & SN_4BYTE_OFFSET_FLAG)) {
/* Insert two dummy bytes that will be overwritten shortly. */
/* Insert three dummy bytes that will be overwritten shortly. */
jssrcnote dummy = 0;
if (!(sn = notes.insert(sn, dummy)) ||
!(sn = notes.insert(sn, dummy)) ||

@ -61,7 +61,7 @@ namespace js {
M(SRC_ASSIGNOP, "assignop", 0) /* += or another assign-op follows. */ \
M(SRC_TRY, "try", 1) /* JSOP_TRY, offset points to goto at the end of the \
try block. */ \
/* All notes below here are "gettable". See SN_IS_GETTABLE below. */ \
/* All notes above here are "gettable". See SN_IS_GETTABLE below. */ \
M(SRC_COLSPAN, "colspan", 1) /* Number of columns this opcode spans. */ \
M(SRC_NEWLINE, "newline", 0) /* Bytecode follows a source newline. */ \
M(SRC_SETLINE, "setline", 1) /* A file-absolute source line number note. */ \
@ -136,20 +136,52 @@ SN_IS_TERMINATOR(jssrcnote *sn)
#define SN_4BYTE_OFFSET_FLAG 0x80
#define SN_4BYTE_OFFSET_MASK 0x7f
/*
* Negative SRC_COLSPAN offsets are rare, but can arise with for(;;) loops and
* other constructs that generate code in non-source order. They can also arise
* due to failure to update pn->pn_pos.end to be the last child's end -- such
* failures are bugs to fix.
*
* Source note offsets in general must be non-negative and less than 0x800000,
* per the above SN_4BYTE_* definitions. To encode negative colspans, we bias
* them by the offset domain size and restrict non-negative colspans to less
* than half this domain.
*/
#define SN_COLSPAN_DOMAIN ptrdiff_t(1 << 23)
#define SN_OFFSET_BITS 31
#define SN_MAX_OFFSET (((size_t) 1 << SN_OFFSET_BITS) - 1)
#define SN_MAX_OFFSET ((size_t)((ptrdiff_t)SN_4BYTE_OFFSET_FLAG << 24) - 1)
inline bool
SN_REPRESENTABLE_OFFSET(ptrdiff_t offset)
{
return 0 <= offset && size_t(offset) <= SN_MAX_OFFSET;
}
/*
* SRC_COLSPAN values represent changes to the column number. Colspans are
* signed: negative changes arise in describing constructs like for(;;) loops,
* that generate code in non-source order. (Negative colspans also have a
* history of indicating bugs in updating ParseNodes' source locations.)
*
* We store colspans using the same variable-length encoding as offsets,
* described above. However, unlike offsets, colspans are signed, so we truncate
* colspans (SN_COLSPAN_TO_OFFSET) for storage as offsets, and sign-extend
* offsets into colspans when we read them (SN_OFFSET_TO_COLSPAN).
*/
#define SN_COLSPAN_SIGN_BIT (1 << (SN_OFFSET_BITS - 1))
#define SN_MIN_COLSPAN (-SN_COLSPAN_SIGN_BIT)
#define SN_MAX_COLSPAN (SN_COLSPAN_SIGN_BIT - 1)
inline bool
SN_REPRESENTABLE_COLSPAN(ptrdiff_t colspan)
{
return SN_MIN_COLSPAN <= colspan && colspan <= SN_MAX_COLSPAN;
}
inline ptrdiff_t
SN_OFFSET_TO_COLSPAN(ptrdiff_t offset) {
// There should be no bits set outside the field we're going to sign-extend.
MOZ_ASSERT(!(offset & ~((1U << SN_OFFSET_BITS) - 1)));
// Sign-extend the least significant SN_OFFSET_BITS bits.
return (offset ^ SN_COLSPAN_SIGN_BIT) - SN_COLSPAN_SIGN_BIT;
}
inline ptrdiff_t
SN_COLSPAN_TO_OFFSET(ptrdiff_t colspan) {
// Truncate the two's complement colspan, for storage as an offset.
ptrdiff_t offset = colspan & ((1U << SN_OFFSET_BITS) - 1);
// When we read this back, we'd better get the value we stored.
MOZ_ASSERT(SN_OFFSET_TO_COLSPAN(offset) == colspan);
return offset;
}
#define SN_LENGTH(sn) ((js_SrcNoteSpec[SN_TYPE(sn)].arity == 0) ? 1 \
: js_SrcNoteLength(sn))

@ -28,11 +28,9 @@ if (helperThreadCount() > 0) {
// Check handling of columns near the limit of our ability to represent them.
// (This is hardly thorough, but since web content can't set column numbers,
// it's probably not worth it to be thorough.)
const maxColumn = Math.pow(2, 22) - 1;
const maxColumn = Math.pow(2, 30) - 1;
assertEq(evaluate("saveStack().column", { columnNumber: maxColumn }),
maxColumn);
assertEq(evaluate("saveStack().column", { columnNumber: maxColumn + 1 }),
0);
// Check the 'silently zero' behavior when we reach the limit of the srcnotes
// column encoding.

@ -2848,10 +2848,7 @@ js::PCToLineNumber(unsigned startLine, jssrcnote *notes, jsbytecode *code, jsbyt
break;
if (type == SRC_COLSPAN) {
ptrdiff_t colspan = js_GetSrcNoteOffset(sn, 0);
if (colspan >= SN_COLSPAN_DOMAIN / 2)
colspan -= SN_COLSPAN_DOMAIN;
ptrdiff_t colspan = SN_OFFSET_TO_COLSPAN(js_GetSrcNoteOffset(sn, 0));
MOZ_ASSERT(ptrdiff_t(column) + colspan >= 0);
column += colspan;
}

@ -1901,9 +1901,7 @@ SrcNotes(JSContext *cx, HandleScript script, Sprinter *sp)
break;
case SRC_COLSPAN:
colspan = js_GetSrcNoteOffset(sn, 0);
if (colspan >= SN_COLSPAN_DOMAIN / 2)
colspan -= SN_COLSPAN_DOMAIN;
colspan = SN_OFFSET_TO_COLSPAN(js_GetSrcNoteOffset(sn, 0));
Sprint(sp, "%d", colspan);
break;

@ -3598,10 +3598,7 @@ class BytecodeRangeWithPosition : private BytecodeRange
while (!SN_IS_TERMINATOR(sn) && snpc <= frontPC()) {
SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
if (type == SRC_COLSPAN) {
ptrdiff_t colspan = js_GetSrcNoteOffset(sn, 0);
if (colspan >= SN_COLSPAN_DOMAIN / 2)
colspan -= SN_COLSPAN_DOMAIN;
ptrdiff_t colspan = SN_OFFSET_TO_COLSPAN(js_GetSrcNoteOffset(sn, 0));
MOZ_ASSERT(ptrdiff_t(column) + colspan >= 0);
column += colspan;
} if (type == SRC_SETLINE) {