You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The left-to-right argument evaluation order is not respected when Event definition contains indexed fields.
There are two evaluation order issues in the current implementation of log function:
indexed field always evaluate first
indexed field evaluate in reverse order
During log expression parsing, IRnodes of indexed fields in the Event definition will be collected separately from normal data field. It will then be passed into ir_node_for_log() function, the function will also encode both of them separately and make them as the arguments of the output IRnode.
defparse_Log(self):
...
args= [Expr(arg, self.context).ir_nodeforarginto_compile]
topic_ir= []
data_ir= []
forarg, is_indexedinzip(args, event.indexed):
ifis_indexed:
topic_ir.append(arg) # saved into different listelse:
data_ir.append(arg)
returnevents.ir_node_for_log(self.stmt, event, topic_ir, data_ir, self.context)
defir_node_for_log(expr, event, topic_nodes, data_nodes, context):
topics=_encode_log_topics(expr, event.event_id, topic_nodes, context)
data=ir_tuple_from_args(data_nodes)
bufsz=data.typ.abi_type.size_bound()
buf=context.new_internal_variable(get_type_for_exact_size(bufsz))
# encode_data is an IRnode which, cleverly, both encodes the data# and returns the length of the encoded data as a stack item.encode_data=abi_encode(buf, data, context, returns_len=True, bufsz=bufsz)
assertlen(topics) <=4, "too many topics"# sanity checklog_opcode="log"+str(len(topics))
returnIRnode.from_list(
[log_opcode, buf, encode_data] +topics,
add_gas_estimate=_gas_bound(len(topics), bufsz),
annotation=f"LOG event {event.signature}",
)
ir_node_for_log() willoutputanLOGNIRnodewithargumentslookslikebelow:
[buf, ABI_encoded_data, event, topic1, ..., topicN]
SincetopicIRnodesareloadedandputintotheargumentarrayseparately, duringthefinalcompilationfromIRnodestoEVMassembly, theargumentsofLOGNopcodewillbetranslatedintoassemblyinareversedorder, makingtheindexedfieldsalwaysbeingevaluatedfirstbutinareversedorder.
```pythondef_compile_to_assembly(code, withargs=None, existing_labels=None, break_dest=None, height=0):
...
ifisinstance(code.value, str) andcode.value.upper() inget_opcodes():
o= []
fori, cinenumerate(code.args[::-1]):
o.extend(_compile_to_assembly(c, withargs, existing_labels, break_dest, height+i))
o.append(code.value.upper())
returno
...
This issue is an interesting intersection of multiple root cause, from the separate parsing of different fields within an event to assembly emission order of irnode args. So naturally part of it would overlap some of the known issues such as reversed order of side effects.
Aside from the issue itself, we especially want to highlight vyper currently lacks a clear definition of evaluation order (left-to-right is the rule of thumb, but there are quite a few exceptions). For example, judging from the code, assign should evaluated rhs (value) before lhs (assigned var), and augassign should evaluate lhs (augmented value) before rhs (delta). When it comes to subscript, the rules are even more complex, where parent should be evaluated before key, but in the case where parent resolves to a storage variable, the evaluation will be done lazily (the code below logs ("sideeffect1", "sideeffect2", 1) since the self.hm1 returned from the ifexp is merely a reference to storage address, and only dereferenced after key is evaluated).
Without a proper definition of evaluation order, developers will often make their own assumptions (which may or may not be correct) while writing code, and it also makes it harder for auditors to decide whether a specific implementation should be considered buggy. Thus we would suggest vyper compiler devs to develop rules for evaluation orders to further improve the language.
Impact Details
Proof of concept
In this case, the evaluation order is not left-to-right, instead, the order is l -> j -> i -> k
credits: @anatomist (Whitehat) for Attackathon | Ethereum Protocol
reportid: #38855
The left-to-right argument evaluation order is not respected when Event definition contains indexed fields.
There are two evaluation order issues in the current implementation of log function:
During log expression parsing, IRnodes of indexed fields in the Event definition will be collected separately from normal data field. It will then be passed into ir_node_for_log() function, the function will also encode both of them separately and make them as the arguments of the output IRnode.
This issue is an interesting intersection of multiple root cause, from the separate parsing of different fields within an event to assembly emission order of irnode args. So naturally part of it would overlap some of the known issues such as reversed order of side effects.
Aside from the issue itself, we especially want to highlight vyper currently lacks a clear definition of evaluation order (left-to-right is the rule of thumb, but there are quite a few exceptions). For example, judging from the code, assign should evaluated rhs (value) before lhs (assigned var), and augassign should evaluate lhs (augmented value) before rhs (delta). When it comes to subscript, the rules are even more complex, where parent should be evaluated before key, but in the case where parent resolves to a storage variable, the evaluation will be done lazily (the code below logs ("sideeffect1", "sideeffect2", 1) since the self.hm1 returned from the ifexp is merely a reference to storage address, and only dereferenced after key is evaluated).
Without a proper definition of evaluation order, developers will often make their own assumptions (which may or may not be correct) while writing code, and it also makes it harder for auditors to decide whether a specific implementation should be considered buggy. Thus we would suggest vyper compiler devs to develop rules for evaluation orders to further improve the language.
Impact Details
Proof of concept
In this case, the evaluation order is not left-to-right, instead, the order is l -> j -> i -> k
The text was updated successfully, but these errors were encountered: