|
3 | 3 | import { compare } from "./compare";
|
4 | 4 | import * as expressions from "./Expr";
|
5 | 5 | import * as log from "./LogFile";
|
| 6 | +import NameMap from "./NameMap"; |
6 | 7 | import { TraceParseError } from "./TraceParseError";
|
7 | 8 | import * as type from "./Type";
|
8 | 9 | import { group } from "./utils";
|
9 | 10 |
|
10 |
| -export function parseExpr(expr: log.Expr): expressions.Expr |
11 |
| -{ |
12 |
| - const exprType = parseType(expr.namedSub.type); |
13 |
| - switch (expr.id) { |
14 |
| - case "constant": |
15 |
| - return new expressions.Constant(expr.namedSub.value.id, exprType); |
16 |
| - case "symbol": |
17 |
| - // Get base name |
18 |
| - if (expr.comment === undefined) |
19 |
| - throw new TraceParseError("No comment on symbol so can't get base_name"); |
20 |
| - return new expressions.Symbol(expr.namedSub.identifier.id, expr.comment["#base_name"].id, exprType); |
21 |
| - case "member": { |
22 |
| - // Get the object we're getting a member from |
23 |
| - const objectExpr = parseExpr(expr.sub[0]); |
24 |
| - const objectType = objectExpr.type; |
25 |
| - // Get the name of the member |
26 |
| - const memberName = expr.namedSub.component_name.id; |
27 |
| - // Access the non-tainted primitive superclass |
28 |
| - if (memberName === primitiveTaintWrapperMemberName) { |
29 |
| - if (objectType.taintVariables.length === 0) |
30 |
| - // Shouldn't look for primitive superclass in non-tainted type |
31 |
| - throw new TraceParseError("Got a primitive taint wrapper parent member expression for something that wasn't a TaintedType"); |
32 |
| - return objectExpr; |
33 |
| - } |
34 |
| - // Access a taint variable |
35 |
| - const taintToken = getTaintTokenName(memberName, exprType); |
36 |
| - if (taintToken !== undefined) { |
37 |
| - if (!objectType.taintVariables.some((taintVariable) => taintVariable === taintToken)) |
38 |
| - throw new TraceParseError("Found taint variable that doesn't exist on type"); |
39 |
| - return new expressions.Taint(objectExpr, taintToken, exprType); |
| 11 | +export default class LogFileParser { |
| 12 | + public parseExpr(expr: log.Expr): expressions.Expr |
| 13 | + { |
| 14 | + const exprType = this.parseType(expr.namedSub.type); |
| 15 | + switch (expr.id) { |
| 16 | + case "constant": |
| 17 | + return new expressions.Constant(expr.namedSub.value.id, exprType); |
| 18 | + case "symbol": |
| 19 | + // Get base name |
| 20 | + if (expr.comment === undefined) |
| 21 | + throw new TraceParseError("No comment on symbol so can't get base_name"); |
| 22 | + return new expressions.Symbol(expr.namedSub.identifier.id, expr.comment["#base_name"].id, exprType); |
| 23 | + case "member": { |
| 24 | + // Get the object we're getting a member from |
| 25 | + const objectExpr = this.parseExpr(expr.sub[0]); |
| 26 | + const objectType = objectExpr.type; |
| 27 | + // Get the name of the member |
| 28 | + const memberName = expr.namedSub.component_name.id; |
| 29 | + // Access the non-tainted primitive superclass |
| 30 | + if (memberName === primitiveTaintWrapperMemberName) { |
| 31 | + if (objectType.taintVariables.length === 0) |
| 32 | + // Shouldn't look for primitive superclass in non-tainted type |
| 33 | + throw new TraceParseError("Got a primitive taint wrapper parent member expression for something that wasn't a TaintedType"); |
| 34 | + return objectExpr; |
| 35 | + } |
| 36 | + // Access a taint variable |
| 37 | + const taintToken = getTaintTokenName(memberName, exprType); |
| 38 | + if (taintToken !== undefined) { |
| 39 | + if (!objectType.taintVariables.some((taintVariable) => taintVariable === taintToken)) |
| 40 | + throw new TraceParseError("Found taint variable that doesn't exist on type"); |
| 41 | + return new expressions.Taint(objectExpr, taintToken, exprType); |
| 42 | + } |
| 43 | + // Shouldn't look for members in non-struct |
| 44 | + if (!(objectType instanceof type.Struct)) |
| 45 | + throw new TraceParseError("Got a member expression for something of non-struct type"); |
| 46 | + // Check if this is an access to a base class, i.e. the memberName matches the result type |
| 47 | + const baseType = getBaseType(memberName, exprType); |
| 48 | + if (baseType !== undefined) { |
| 49 | + if (!objectType.bases.some((base) => base.compare(baseType) === 0)) |
| 50 | + throw new TraceParseError("Found base accessor for non-base"); |
| 51 | + return new expressions.Downcast(objectExpr, baseType); |
| 52 | + } |
| 53 | + return new expressions.Member(objectExpr, memberName, exprType); |
40 | 54 | }
|
41 |
| - // Shouldn't look for members in non-struct |
42 |
| - if (!(objectType instanceof type.Struct)) |
43 |
| - throw new TraceParseError("Got a member expression for something of non-struct type"); |
44 |
| - // Check if this is an access to a base class, i.e. the memberName matches the result type |
45 |
| - const baseType = getBaseType(memberName, exprType); |
46 |
| - if (baseType !== undefined) { |
47 |
| - if (!objectType.bases.some((base) => base.compare(baseType) === 0)) |
48 |
| - throw new TraceParseError("Found base accessor for non-base"); |
49 |
| - return new expressions.Downcast(objectExpr, baseType); |
| 55 | + case "index": { |
| 56 | + return new expressions.Index(this.parseExpr(expr.sub[0]), this.parseExpr(expr.sub[1]), exprType); |
50 | 57 | }
|
51 |
| - return new expressions.Member(objectExpr, memberName, exprType); |
52 |
| - } |
53 |
| - case "index": { |
54 |
| - return new expressions.Index(parseExpr(expr.sub[0]), parseExpr(expr.sub[1]), exprType); |
55 | 58 | }
|
56 | 59 | }
|
57 |
| -} |
58 | 60 |
|
59 |
| -function parseType(logType: log.Type): type.Type { |
60 |
| - switch (logType.id) { |
61 |
| - case "bool": return type.booleanType; |
62 |
| - case "c_bool": return type.cBoolean; |
63 |
| - case "signedbv": return new type.Integer(parseInt(logType.namedSub.width.id, 10), true, []); |
64 |
| - case "unsignedbv": return new type.Integer(parseInt(logType.namedSub.width.id, 10), false, []); |
65 |
| - case "string": return type.stringType; |
66 |
| - case "pointer": return new type.Pointer(parseType(logType.sub[0]), []); |
67 |
| - case "struct": { |
68 |
| - // Get bases, fields and taint variables |
69 |
| - const members = group( |
70 |
| - logType.namedSub.components.sub |
71 |
| - .map((member) => new type.Field(member.namedSub.name.id, member.namedSub.pretty_name.id, parseType(member.namedSub.type))), |
72 |
| - (field) => { |
73 |
| - if (getTaintTokenName(field.name, field.type) !== undefined) |
74 |
| - return "taint"; |
75 |
| - else if (getBaseType(field.name, field.type) !== undefined) |
76 |
| - return "base"; |
77 |
| - else |
78 |
| - return "field"; |
79 |
| - }); |
80 |
| - // Get bases |
81 |
| - let bases = logType.namedSub.bases === undefined ? [] : logType.namedSub.bases.sub |
82 |
| - .filter((base) => type.Struct.has(base.namedSub.type.namedSub.identifier.id)) |
83 |
| - .map((base) => type.Struct.get(base.namedSub.type.namedSub.identifier.id)); |
84 |
| - const baseMembers = members.get("base"); |
85 |
| - const extraBases = baseMembers === undefined ? [] |
86 |
| - : baseMembers |
87 |
| - .map((field) => getBaseType(field.name, field.type) as type.Struct) |
88 |
| - .filter((extraBase) => bases.findIndex((base) => compare(extraBase, base) === 0) === -1); |
89 |
| - bases = bases.concat(extraBases); |
90 |
| - // Get fields |
91 |
| - const fieldMembers = members.get("field"); |
92 |
| - const fields = fieldMembers === undefined ? [] : fieldMembers; |
93 |
| - // Get taint variables |
94 |
| - const taintVariableMembers = members.get("taint"); |
95 |
| - const taintVariables = taintVariableMembers === undefined ? [] |
96 |
| - : taintVariableMembers.map((field) => getTaintTokenName(field.name, field.type) as string); |
97 |
| - // Get struct name |
98 |
| - const structName = getStructName(logType); |
99 |
| - // Check for primitive type wrappers |
100 |
| - if (isPrimitiveTaintWrapper(logType)) { |
101 |
| - if (fields.length !== 1 || fields[0].name !== primitiveTaintWrapperMemberName) |
102 |
| - throw new TraceParseError(primitiveTaintWrapperPrefix + "* type doesn't have a single field called " + primitiveTaintWrapperMemberName); |
103 |
| - if (taintVariables.length === 0) |
104 |
| - throw new TraceParseError("A struct that contains a " + primitiveTaintWrapperMemberName + " member has no taint variables"); |
105 |
| - const base = fields[0].type; |
106 |
| - if (bases.length === 1) { |
107 |
| - if (bases[0] !== base) |
108 |
| - throw new TraceParseError("Type of taint wrapper super was not the base type"); |
109 |
| - } else if (bases.length !== 0) |
110 |
| - throw new TraceParseError(primitiveTaintWrapperPrefix + "* type has more than one base"); |
111 |
| - if (base.taintVariables.length === 0) |
112 |
| - base.taintVariables = taintVariables; |
113 |
| - else if (compare(base.taintVariables, taintVariables) !== 0) |
114 |
| - throw new TraceParseError("Found taint variables didn't match those on the base"); |
115 |
| - return base; |
| 61 | + private parseType(logType: log.Type): type.Type { |
| 62 | + switch (logType.id) { |
| 63 | + case "bool": return type.booleanType; |
| 64 | + case "c_bool": return type.cBoolean; |
| 65 | + case "signedbv": return new type.Integer(parseInt(logType.namedSub.width.id, 10), true, []); |
| 66 | + case "unsignedbv": return new type.Integer(parseInt(logType.namedSub.width.id, 10), false, []); |
| 67 | + case "string": return type.stringType; |
| 68 | + case "pointer": return new type.Pointer(this.parseType(logType.sub[0]), []); |
| 69 | + case "struct": { |
| 70 | + // Get bases, fields and taint variables |
| 71 | + const members = group( |
| 72 | + logType.namedSub.components.sub |
| 73 | + .map((member) => new type.Field(member.namedSub.name.id, member.namedSub.pretty_name.id, this.parseType(member.namedSub.type))), |
| 74 | + (field) => { |
| 75 | + if (getTaintTokenName(field.name, field.type) !== undefined) |
| 76 | + return "taint"; |
| 77 | + else if (getBaseType(field.name, field.type) !== undefined) |
| 78 | + return "base"; |
| 79 | + else |
| 80 | + return "field"; |
| 81 | + }); |
| 82 | + // Get bases |
| 83 | + let bases = logType.namedSub.bases === undefined ? [] : logType.namedSub.bases.sub |
| 84 | + .filter((base) => this.structExists(base.namedSub.type.namedSub.identifier.id)) |
| 85 | + .map((base) => this.getStruct(base.namedSub.type.namedSub.identifier.id)); |
| 86 | + const baseMembers = members.get("base"); |
| 87 | + const extraBases = baseMembers === undefined ? [] |
| 88 | + : baseMembers |
| 89 | + .map((field) => getBaseType(field.name, field.type) as type.Struct) |
| 90 | + .filter((extraBase) => bases.findIndex((base) => compare(extraBase, base) === 0) === -1); |
| 91 | + bases = bases.concat(extraBases); |
| 92 | + // Get fields |
| 93 | + const fieldMembers = members.get("field"); |
| 94 | + const fields = fieldMembers === undefined ? [] : fieldMembers; |
| 95 | + // Get taint variables |
| 96 | + const taintVariableMembers = members.get("taint"); |
| 97 | + const taintVariables = taintVariableMembers === undefined ? [] |
| 98 | + : taintVariableMembers.map((field) => getTaintTokenName(field.name, field.type) as string); |
| 99 | + // Get struct name |
| 100 | + const structName = getStructName(logType); |
| 101 | + // Check for primitive type wrappers |
| 102 | + if (isPrimitiveTaintWrapper(logType)) { |
| 103 | + if (fields.length !== 1 || fields[0].name !== primitiveTaintWrapperMemberName) |
| 104 | + throw new TraceParseError(primitiveTaintWrapperPrefix + "* type doesn't have a single field called " + primitiveTaintWrapperMemberName); |
| 105 | + if (taintVariables.length === 0) |
| 106 | + throw new TraceParseError("A struct that contains a " + primitiveTaintWrapperMemberName + " member has no taint variables"); |
| 107 | + const base = fields[0].type; |
| 108 | + if (bases.length === 1) { |
| 109 | + if (bases[0] !== base) |
| 110 | + throw new TraceParseError("Type of taint wrapper super was not the base type"); |
| 111 | + } else if (bases.length !== 0) |
| 112 | + throw new TraceParseError(primitiveTaintWrapperPrefix + "* type has more than one base"); |
| 113 | + if (base.taintVariables.length === 0) |
| 114 | + base.taintVariables = taintVariables; |
| 115 | + else if (compare(base.taintVariables, taintVariables) !== 0) |
| 116 | + throw new TraceParseError("Found taint variables didn't match those on the base"); |
| 117 | + return base; |
| 118 | + } |
| 119 | + // Get existing element from map and initialize |
| 120 | + const struct = this.getStruct(structName.id); |
| 121 | + struct.initialize(bases, fields, taintVariables); |
| 122 | + // Check for primitive taint wrapper member field in non-taint structure |
| 123 | + if (struct.fields.get(primitiveTaintWrapperMemberName) !== undefined) |
| 124 | + throw new TraceParseError(struct.name + " contains a field called " + primitiveTaintWrapperMemberName); |
| 125 | + // Return the struct, with taint if applicable |
| 126 | + return struct; |
116 | 127 | }
|
117 |
| - // Get existing element from map and initialize |
118 |
| - const struct = type.Struct.get(structName.id); |
119 |
| - struct.initialize(bases, fields, taintVariables); |
120 |
| - // Check for primitive taint wrapper member field in non-taint structure |
121 |
| - if (struct.fields.get(primitiveTaintWrapperMemberName) !== undefined) |
122 |
| - throw new TraceParseError(struct.name + " contains a field called " + primitiveTaintWrapperMemberName); |
123 |
| - // Return the struct, with taint if applicable |
124 |
| - return struct; |
| 128 | + case "symbol_type": |
| 129 | + let name = logType.namedSub.identifier.id; |
| 130 | + if (name.startsWith(primitiveTaintWrapperPrefix)) |
| 131 | + name = name.substr(primitiveTaintWrapperPrefix.length); |
| 132 | + return this.getStruct(name); |
| 133 | + // default: |
| 134 | + // throw new TraceParseError("Unexpected id on type: " + logType.id); |
125 | 135 | }
|
126 |
| - case "symbol_type": |
127 |
| - let name = logType.namedSub.identifier.id; |
128 |
| - if (name.startsWith(primitiveTaintWrapperPrefix)) |
129 |
| - name = name.substr(primitiveTaintWrapperPrefix.length); |
130 |
| - return type.Struct.get(name); |
131 |
| - // default: |
132 |
| - // throw new TraceParseError("Unexpected id on type: " + logType.id); |
133 | 136 | }
|
134 |
| -} |
135 | 137 |
|
| 138 | + private readonly structCache = new NameMap<type.Struct>(); |
| 139 | + |
| 140 | + public structExists(name: string): boolean { |
| 141 | + return this.structCache.has(name); |
| 142 | + } |
| 143 | + |
| 144 | + public createStruct(name: string) { |
| 145 | + const struct = new type.Struct(name); |
| 146 | + this.structCache.add(struct); |
| 147 | + return struct; |
| 148 | + } |
| 149 | + |
| 150 | + public getStruct(name: string): type.Struct { |
| 151 | + const result = this.structCache.get(name); |
| 152 | + if (result === undefined) |
| 153 | + throw new Error("Request for missing struct: " + name); |
| 154 | + return result; |
| 155 | + } |
| 156 | + |
| 157 | + public get allStructs(): Iterable<type.Struct> { |
| 158 | + return this.structCache; |
| 159 | + } |
| 160 | +} |
136 | 161 |
|
137 | 162 | export const primitiveTaintWrapperPrefix = "@__CPROVER_primitive_taint_wrapper_";
|
138 | 163 | export function isPrimitiveTaintWrapper(logType: log.StructType) {
|
|
0 commit comments