Skip to content

Commit d3db676

Browse files
authored
fix: Backport recent 8.x fixes to 7.x (#2422)
1 parent fe82f72 commit d3db676

15 files changed

Lines changed: 1781 additions & 94 deletions

bench/data/static_pbjs.js

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,17 @@ $root.Test = (function() {
4646
long = 0;
4747
if (long > $Reader.recursionLimit)
4848
throw Error("maximum nesting depth exceeded");
49-
var end = length === undefined ? reader.len : reader.pos + length, message = new $root.Test();
49+
var end, message;
50+
if (length === undefined)
51+
end = reader.len;
52+
else {
53+
end = reader.pos + length;
54+
if (end > reader.len)
55+
throw RangeError("index out of range");
56+
length = reader.len;
57+
reader.len = end;
58+
}
59+
message = new $root.Test();
5060
while (reader.pos < end) {
5161
var tag = reader.uint32();
5262
if (tag === error)
@@ -73,6 +83,11 @@ $root.Test = (function() {
7383
break;
7484
}
7585
}
86+
if (length !== undefined) {
87+
if (reader.pos !== end)
88+
throw RangeError("index out of range");
89+
reader.len = length;
90+
}
7691
return message;
7792
};
7893

@@ -112,7 +127,17 @@ $root.Test = (function() {
112127
long = 0;
113128
if (long > $Reader.recursionLimit)
114129
throw Error("maximum nesting depth exceeded");
115-
var end = length === undefined ? reader.len : reader.pos + length, message = new $root.Test.Inner();
130+
var end, message;
131+
if (length === undefined)
132+
end = reader.len;
133+
else {
134+
end = reader.pos + length;
135+
if (end > reader.len)
136+
throw RangeError("index out of range");
137+
length = reader.len;
138+
reader.len = end;
139+
}
140+
message = new $root.Test.Inner();
116141
while (reader.pos < end) {
117142
var tag = reader.uint32();
118143
if (tag === error)
@@ -135,6 +160,11 @@ $root.Test = (function() {
135160
break;
136161
}
137162
}
163+
if (length !== undefined) {
164+
if (reader.pos !== end)
165+
throw RangeError("index out of range");
166+
reader.len = length;
167+
}
138168
return message;
139169
};
140170

@@ -174,7 +204,17 @@ $root.Test = (function() {
174204
long = 0;
175205
if (long > $Reader.recursionLimit)
176206
throw Error("maximum nesting depth exceeded");
177-
var end = length === undefined ? reader.len : reader.pos + length, message = new $root.Test.Inner.InnerInner();
207+
var end, message;
208+
if (length === undefined)
209+
end = reader.len;
210+
else {
211+
end = reader.pos + length;
212+
if (end > reader.len)
213+
throw RangeError("index out of range");
214+
length = reader.len;
215+
reader.len = end;
216+
}
217+
message = new $root.Test.Inner.InnerInner();
178218
while (reader.pos < end) {
179219
var tag = reader.uint32();
180220
if (tag === error)
@@ -197,6 +237,11 @@ $root.Test = (function() {
197237
break;
198238
}
199239
}
240+
if (length !== undefined) {
241+
if (reader.pos !== end)
242+
throw RangeError("index out of range");
243+
reader.len = length;
244+
}
200245
return message;
201246
};
202247

@@ -257,7 +302,17 @@ $root.Outer = (function() {
257302
long = 0;
258303
if (long > $Reader.recursionLimit)
259304
throw Error("maximum nesting depth exceeded");
260-
var end = length === undefined ? reader.len : reader.pos + length, message = new $root.Outer();
305+
var end, message;
306+
if (length === undefined)
307+
end = reader.len;
308+
else {
309+
end = reader.pos + length;
310+
if (end > reader.len)
311+
throw RangeError("index out of range");
312+
length = reader.len;
313+
reader.len = end;
314+
}
315+
message = new $root.Outer();
261316
while (reader.pos < end) {
262317
var tag = reader.uint32();
263318
if (tag === error)
@@ -268,8 +323,14 @@ $root.Outer = (function() {
268323
message.bool = [];
269324
if ((tag & 7) === 2) {
270325
var end2 = reader.uint32() + reader.pos;
326+
if (end2 > reader.len)
327+
throw RangeError("index out of range");
328+
reader.len = end2;
271329
while (reader.pos < end2)
272330
message.bool.push(reader.bool());
331+
if (reader.pos !== end2)
332+
throw RangeError("index out of range");
333+
reader.len = end;
273334
} else
274335
message.bool.push(reader.bool());
275336
break;
@@ -283,6 +344,11 @@ $root.Outer = (function() {
283344
break;
284345
}
285346
}
347+
if (length !== undefined) {
348+
if (reader.pos !== end)
349+
throw RangeError("index out of range");
350+
reader.len = length;
351+
}
286352
return message;
287353
};
288354

src/decoder.js

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,17 @@ function decoder(mtype) {
2222
("if(n===undefined)n=0")
2323
("if(n>Reader.recursionLimit)")
2424
("throw Error(\"maximum nesting depth exceeded\")")
25-
("var c=l===undefined?r.len:r.pos+l,m=new this.ctor" + (mtype.fieldsArray.filter(function(field) { return field.map; }).length ? ",k,value" : ""))
25+
("var c,m" + (mtype.fieldsArray.filter(function(field) { return field.map; }).length ? ",k,value" : ""))
26+
("if(l===undefined)")
27+
("c=r.len")
28+
("else{")
29+
("c=r.pos+l")
30+
("if(c>r.len)")
31+
("throw RangeError(\"index out of range\")")
32+
("l=r.len")
33+
("r.len=c")
34+
("}")
35+
("m=new this.ctor")
2636
("while(r.pos<c){")
2737
("var t=r.uint32()")
2838
("if(t===e)")
@@ -40,7 +50,10 @@ function decoder(mtype) {
4050
if (field.map) { gen
4151
("if(%s===util.emptyObject)", ref)
4252
("%s={}", ref)
43-
("var c2 = r.uint32()+r.pos");
53+
("var c2=r.uint32()+r.pos")
54+
("if(c2>r.len)")
55+
("throw RangeError(\"index out of range\")")
56+
("r.len=c2");
4457

4558
if (types.defaults[field.keyType] !== undefined) gen
4659
("k=%j", types.defaults[field.keyType]);
@@ -70,7 +83,10 @@ function decoder(mtype) {
7083
("r.skipType(tag2&7,n)")
7184
("break")
7285
("}")
73-
("}");
86+
("}")
87+
("if(r.pos!==c2)")
88+
("throw RangeError(\"index out of range\")")
89+
("r.len=c");
7490

7591
if (types.long[field.keyType] !== undefined) gen
7692
("%s[typeof k===\"object\"?util.longToHash(k):k]=value", ref);
@@ -92,8 +108,14 @@ function decoder(mtype) {
92108
if (types.packed[type] !== undefined) gen
93109
("if((t&7)===2){")
94110
("var c2=r.uint32()+r.pos")
111+
("if(c2>r.len)")
112+
("throw RangeError(\"index out of range\")")
113+
("r.len=c2")
95114
("while(r.pos<c2)")
96115
("%s.push(r.%s())", ref, type)
116+
("if(r.pos!==c2)")
117+
("throw RangeError(\"index out of range\")")
118+
("r.len=c")
97119
("}else");
98120

99121
// Non-packed
@@ -121,6 +143,13 @@ function decoder(mtype) {
121143
("}")
122144
("}");
123145

146+
gen
147+
("if(l!==undefined){")
148+
("if(r.pos!==c)")
149+
("throw RangeError(\"index out of range\")")
150+
("r.len=l")
151+
("}");
152+
124153
// Field presence
125154
for (i = 0; i < mtype._fieldsArray.length; ++i) {
126155
var rfield = mtype._fieldsArray[i];

src/reader.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,20 @@ Reader.create = create();
7878

7979
Reader.prototype._slice = util.Array.prototype.subarray || /* istanbul ignore next */ util.Array.prototype.slice;
8080

81+
function readVarint32NearEnd(reader) {
82+
// Safely read up to four bytes of a varint32 near the reader limit
83+
var value = 0;
84+
for (var i = 0; i < 4; ++i) {
85+
if (reader.pos >= reader.len)
86+
throw indexOutOfRange(reader);
87+
var b = reader.buf[reader.pos++];
88+
value = (value | (b & 127) << i * 7) >>> 0;
89+
if (b < 128)
90+
return value;
91+
}
92+
throw indexOutOfRange(reader);
93+
}
94+
8195
/**
8296
* Reads a varint as an unsigned 32 bit value.
8397
* @function
@@ -86,6 +100,12 @@ Reader.prototype._slice = util.Array.prototype.subarray || /* istanbul ignore ne
86100
Reader.prototype.uint32 = (function read_uint32_setup() {
87101
var value = 4294967295; // optimizer type-hint, tends to deopt otherwise (?!)
88102
return function read_uint32() {
103+
if (this.len - this.pos < 5) {
104+
if (this.pos >= this.len)
105+
throw indexOutOfRange(this);
106+
if (this.buf[this.pos] >= 128)
107+
return readVarint32NearEnd(this);
108+
}
89109
value = ( this.buf[this.pos] & 127 ) >>> 0; if (this.buf[this.pos++] < 128) return value;
90110
value = (value | (this.buf[this.pos] & 127) << 7) >>> 0; if (this.buf[this.pos++] < 128) return value;
91111
value = (value | (this.buf[this.pos] & 127) << 14) >>> 0; if (this.buf[this.pos++] < 128) return value;

src/wrappers.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ var Message = require("./message"),
4040
wrappers[".google.protobuf.Any"] = {
4141

4242
fromObject: function(object, depth) {
43+
if (depth === undefined)
44+
depth = 0;
45+
if (depth > util.recursionLimit)
46+
throw Error("max depth exceeded");
4347

4448
// unwrap value type if mapped
4549
if (object && object["@type"]) {
@@ -57,7 +61,7 @@ wrappers[".google.protobuf.Any"] = {
5761
}
5862
return this.create({
5963
type_url: type_url,
60-
value: type.encode(type.fromObject(object, depth === undefined ? 1 : depth + 1)).finish()
64+
value: type.encode(type.fromObject(object, depth + 1)).finish()
6165
});
6266
}
6367
}

tests/api_decoder_bounds.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
var tape = require("tape");
2+
3+
var protobuf = require("..");
4+
5+
tape.test("decoder respects enclosing message boundaries", function(test) {
6+
var constructed;
7+
var Inner = new protobuf.Type("Inner")
8+
.add(new protobuf.Field("ix", 1, "int32"))
9+
.add(new protobuf.Field("iy", 2, "int32"))
10+
.add(new protobuf.Field("iz", 3, "int32"));
11+
Inner.ctor = function Inner() {
12+
constructed = this;
13+
};
14+
var Mid = new protobuf.Type("Mid")
15+
.add(new protobuf.Field("inner", 1, "Inner"))
16+
.add(new protobuf.Field("mx", 2, "int32"))
17+
.add(new protobuf.Field("my", 3, "int32"))
18+
.add(Inner);
19+
var Outer = new protobuf.Type("Outer")
20+
.add(new protobuf.Field("mid", 1, "Mid"))
21+
.add(new protobuf.Field("ox", 2, "int32"))
22+
.add(new protobuf.Field("oy", 3, "int32"))
23+
.add(Mid);
24+
25+
test.throws(function() {
26+
Outer.decode([ 0x0a, 0x08, 0x0a, 0x08, 0x08, 0x07, 0x10, 0x65, 0x18, 0x78, 0x10, 0x64, 0x18, 0x7b ]);
27+
}, RangeError, "rejects a nested message that consumes its parent's following fields");
28+
test.notOk(constructed, "rejects the nested length before constructing the message");
29+
30+
test.throws(function() {
31+
Outer.decode([ 0x0a, 0x04, 0x0a, 0x02, 0x08, 0x80, 0x10, 0x64 ]);
32+
}, RangeError, "rejects a scalar value that crosses the nested boundary");
33+
test.ok(constructed, "constructs a message within a valid declared length");
34+
test.notOk(Object.hasOwnProperty.call(constructed, "ix"), "does not assign bytes from the parent field");
35+
test.end();
36+
});
37+
38+
tape.test("decoder respects map-entry boundaries", function(test) {
39+
var Type = new protobuf.Type("MapMessage")
40+
.add(new protobuf.MapField("values", 1, "string", "uint32"))
41+
.add(new protobuf.Field("after", 2, "uint32"))
42+
.add(new protobuf.Field("tail", 3, "uint32"));
43+
44+
test.throws(function() {
45+
Type.decode([ 0x0a, 0x03, 0x0a, 0x02, 0x41, 0x10, 0x18, 0x07 ]);
46+
}, RangeError, "rejects a map key that consumes the following field");
47+
test.end();
48+
});
49+
50+
tape.test("decoder respects packed-field boundaries", function(test) {
51+
var Type = new protobuf.Type("PackedMessage")
52+
.add(new protobuf.Field("values", 1, "uint32", "repeated", { packed: true }))
53+
.add(new protobuf.Field("after", 2, "uint32"))
54+
.add(new protobuf.Field("tail", 3, "uint32"));
55+
56+
test.throws(function() {
57+
Type.decode([ 0x0a, 0x01, 0x80, 0x10, 0x18, 0x07 ]);
58+
}, RangeError, "rejects a packed varint that consumes the following field");
59+
test.end();
60+
});
61+
62+
tape.test("decoder respects group boundaries", function(test) {
63+
var Type = protobuf.parse([
64+
"syntax = \"proto2\";",
65+
"message Outer { optional Inner inner = 1; optional int32 after = 2; }",
66+
"message Inner { optional group Child = 1 { optional int32 value = 2; } }"
67+
].join("\n")).root.lookupType("Outer");
68+
69+
test.throws(function() {
70+
Type.decode([ 0x0a, 0x01, 0x0b, 0x0c, 0x10, 0x07 ]);
71+
}, RangeError, "rejects an end-group tag beyond the parent boundary");
72+
test.end();
73+
});
74+
75+
tape.test("decoder respects unknown fixed-width field boundaries", function(test) {
76+
var Inner = new protobuf.Type("Inner");
77+
var Outer = new protobuf.Type("Outer")
78+
.add(new protobuf.Field("inner", 1, "Inner"))
79+
.add(new protobuf.Field("after", 2, "uint32"))
80+
.add(new protobuf.Field("tail", 3, "uint32"))
81+
.add(Inner);
82+
83+
test.throws(function() {
84+
Outer.decode([ 0x0a, 0x01, 0x09, 0x10, 1, 0x10, 2, 0x10, 3, 0x10, 4, 0x18, 7 ]);
85+
}, RangeError, "rejects an unknown fixed64 field that crosses the parent boundary");
86+
test.end();
87+
});

tests/comp_google_protobuf_any.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,20 @@ tape.test("google.protobuf.Any - toObject recursion limit", function(test) {
9797

9898
test.end();
9999
});
100+
101+
tape.test("google.protobuf.Any - fromObject recursion limit", function(test) {
102+
103+
var recursionLimit = protobuf.util.recursionLimit;
104+
protobuf.util.recursionLimit = 3;
105+
try {
106+
test.throws(function() {
107+
Any.fromObject({
108+
"@type": "type.googleapis.com/google.protobuf.Any"
109+
});
110+
}, /max depth exceeded/, "should reject excessive Any object expansion depth");
111+
} finally {
112+
protobuf.util.recursionLimit = recursionLimit;
113+
}
114+
115+
test.end();
116+
});

0 commit comments

Comments
 (0)