-
Notifications
You must be signed in to change notification settings - Fork 94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Modify ReturnStatement to prevent a UaF #76
base: master
Are you sure you want to change the base?
Changes from all commits
a2b0289
7a140b3
2378b95
e40d46c
da64643
e32b352
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
import * as ts from 'typescript'; | ||
import { CodeTemplate, CodeTemplateFactory, CTemplateBase } from '../template'; | ||
import { CProgram, IScope} from '../program'; | ||
import { CProgram, IScope } from '../program'; | ||
import { ArrayType, NumberVarType, StringVarType } from '../types/ctypes'; | ||
import { CVariable, CVariableDeclaration, CVariableDestructors } from './variable'; | ||
import { CExpression, CCondition } from './expressions'; | ||
|
@@ -71,17 +71,48 @@ export class CEmptyStatement { | |
} | ||
|
||
@CodeTemplate(` | ||
{destructors} | ||
return {expression}; | ||
{#if returnTypeAndVar} | ||
{ | ||
{returnTypeAndVar} = {expression}; | ||
{destructors} | ||
return {returnTemp}; | ||
} | ||
{#else} | ||
{destructors} | ||
return {expression}; | ||
{/if} | ||
`, ts.SyntaxKind.ReturnStatement) | ||
export class CReturnStatement extends CTemplateBase { | ||
public expression: CExpression; | ||
public destructors: CVariableDestructors; | ||
public retVarName: string = null; | ||
public returnTypeAndVar: string = null; | ||
public returnTemp: string = null; | ||
public closureParams: { name: string, value: CExpression }[] = []; | ||
doCheckVarNeeded(scope: IScope, node: ts.ReturnStatement): boolean { | ||
let s = scope.root.memoryManager.getDestructorsForScope(node); | ||
let result = false; | ||
for (let e of s) { | ||
result = result || new RegExp('\\W' + e.varName + '\\W').test(' ' + node.getFullText() + ' '); | ||
} | ||
return result; | ||
} | ||
constructor(scope: IScope, node: ts.ReturnStatement) { | ||
super(); | ||
this.returnTemp = scope.root.symbolsHelper.addTemp(node, 'returnVal'); | ||
let returnType = scope.root.typeHelper.getCType(node.expression); | ||
Comment on lines
+102
to
+103
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's a good idea to move those two lines also into the if block because otherwise we're reserving the temp variable name even when it is not really needed |
||
if (this.doCheckVarNeeded(scope, node)) { | ||
// todo: make this less hackyy | ||
let fakeVar = new CVariable(scope, '__fake', returnType, { removeStorageSpecifier: true, arraysToPointers: true });; | ||
let fakeVarType = fakeVar.resolve().slice(0, -6).trim(); | ||
this.returnTypeAndVar = fakeVarType; | ||
if (this.returnTypeAndVar.indexOf('{var}') == -1) { | ||
this.returnTypeAndVar += ' {var}'; | ||
} | ||
this.returnTypeAndVar = this.returnTypeAndVar.replace('{var}', this.returnTemp); | ||
Comment on lines
+105
to
+112
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is not only too hacky, it is also not a proper C89 as it produces a variable declaration that is not in the beginning of the block:
it's very easy to fix though, you just need to do smth like this:
pushing the variable into the scope variables will automatically add it's declaration into the right place |
||
} | ||
this.expression = CodeTemplateFactory.createForNode(scope, node.expression); | ||
|
||
this.destructors = new CVariableDestructors(scope, node); | ||
} | ||
} | ||
|
@@ -380,8 +411,7 @@ export class CForInStatement extends CTemplateBase implements IScope { | |
else | ||
this.init = new CElementAccess(scope, node.initializer); | ||
|
||
if (node.statement.kind == ts.SyntaxKind.Block) | ||
{ | ||
if (node.statement.kind == ts.SyntaxKind.Block) { | ||
let block = <ts.Block>node.statement; | ||
for (let s of block.statements) | ||
this.statements.push(CodeTemplateFactory.createForNode(this, s)); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -113,45 +113,43 @@ export class CVariableAllocation extends CTemplateBase { | |
} | ||
|
||
@CodeTemplate(` | ||
{#statements} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't really know, but i know the tests pass... I removed statements because it moved it above the actual return code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tested it and basically the main side effect is that it inserts empty lines in random places if destructors are not within I think if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
my assumption was incorrect I guess we will have to get rid of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. General whitespace handling improvements were done in c27ad6d However, I noticed there's another side effect of removing I'll have to investigate and fix it. |
||
{arrayDestructors => for (gc_i = 0; gc_i < ({this} ? {this}->size : 0); gc_i++) free((void*){this}->data[gc_i]);\n} | ||
{destructors => free({this});\n} | ||
{#if gcArraysCVarName} | ||
for (gc_i = 0; gc_i < {gcArraysCVarName}->size; gc_i++) { | ||
for (gc_j = 0; gc_j < ({gcArraysCVarName}->data[gc_i] ? {gcArraysCVarName}->data[gc_i]->size : 0); gc_j++) | ||
free((void*){gcArraysCVarName}->data[gc_i]->data[gc_j]);\n | ||
free({gcArraysCVarName}->data[gc_i] ? {gcArraysCVarName}->data[gc_i]->data : NULL); | ||
free({gcArraysCVarName}->data[gc_i]); | ||
} | ||
free({gcArraysCVarName}->data); | ||
free({gcArraysCVarName}); | ||
{/if} | ||
{#if gcArraysVarName} | ||
for (gc_i = 0; gc_i < {gcArraysVarName}->size; gc_i++) { | ||
free({gcArraysVarName}->data[gc_i]->data); | ||
free({gcArraysVarName}->data[gc_i]); | ||
} | ||
free({gcArraysVarName}->data); | ||
free({gcArraysVarName}); | ||
{/if} | ||
{#if gcDictsVarName} | ||
for (gc_i = 0; gc_i < {gcDictsVarName}->size; gc_i++) { | ||
free({gcDictsVarName}->data[gc_i]->index->data); | ||
free({gcDictsVarName}->data[gc_i]->index); | ||
free({gcDictsVarName}->data[gc_i]->values->data); | ||
free({gcDictsVarName}->data[gc_i]->values); | ||
free({gcDictsVarName}->data[gc_i]); | ||
} | ||
free({gcDictsVarName}->data); | ||
free({gcDictsVarName}); | ||
{/if} | ||
{#if gcVarName} | ||
for (gc_i = 0; gc_i < {gcVarName}->size; gc_i++) | ||
free({gcVarName}->data[gc_i]); | ||
free({gcVarName}->data); | ||
free({gcVarName}); | ||
{/if} | ||
{/statements}` | ||
{arrayDestructors => for (gc_i = 0; gc_i < ({this} ? {this}->size : 0); gc_i++) free((void*){this}->data[gc_i]);\n} | ||
{destructors => free({this});\n} | ||
{#if gcArraysCVarName} | ||
for (gc_i = 0; gc_i < {gcArraysCVarName}->size; gc_i++) { | ||
for (gc_j = 0; gc_j < ({gcArraysCVarName}->data[gc_i] ? {gcArraysCVarName}->data[gc_i]->size : 0); gc_j++) | ||
free((void*){gcArraysCVarName}->data[gc_i]->data[gc_j]);\n | ||
free({gcArraysCVarName}->data[gc_i] ? {gcArraysCVarName}->data[gc_i]->data : NULL); | ||
free({gcArraysCVarName}->data[gc_i]); | ||
} | ||
free({gcArraysCVarName}->data); | ||
free({gcArraysCVarName}); | ||
{/if} | ||
{#if gcArraysVarName} | ||
for (gc_i = 0; gc_i < {gcArraysVarName}->size; gc_i++) { | ||
free({gcArraysVarName}->data[gc_i]->data); | ||
free({gcArraysVarName}->data[gc_i]); | ||
} | ||
free({gcArraysVarName}->data); | ||
free({gcArraysVarName}); | ||
{/if} | ||
{#if gcDictsVarName} | ||
for (gc_i = 0; gc_i < {gcDictsVarName}->size; gc_i++) { | ||
free({gcDictsVarName}->data[gc_i]->index->data); | ||
free({gcDictsVarName}->data[gc_i]->index); | ||
free({gcDictsVarName}->data[gc_i]->values->data); | ||
free({gcDictsVarName}->data[gc_i]->values); | ||
free({gcDictsVarName}->data[gc_i]); | ||
} | ||
free({gcDictsVarName}->data); | ||
free({gcDictsVarName}); | ||
{/if} | ||
{#if gcVarName} | ||
for (gc_i = 0; gc_i < {gcVarName}->size; gc_i++) | ||
free({gcVarName}->data[gc_i]); | ||
free({gcVarName}->data); | ||
free({gcVarName}); | ||
{/if}` | ||
) | ||
export class CVariableDestructors extends CTemplateBase { | ||
public gcVarName: string = null; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
1 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// regression test for #74 | ||
function return_from_obj() { | ||
let obj = { key: 1 }; | ||
return obj.key; | ||
} | ||
|
||
console.log(return_from_obj()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am afraid relying on a text match here is too fragile and error-prone.
return statement can be huge, for example something like
return function() { /* ... 200 lines of code here ... */ }
, or something likereturn translate("Lorem ipsum dolor ... 5000 characters of random text here")
... it can contain any imaginable text and it will not mean anything.the right way would be to do something similar to what we do in memory.ts i.e. properly analyse the return statement and if a subkey of an object that was scheduled to be disposed in this scope escapes into the upper function, then we need to change the return statement to use the temporary variable.