From 7ca6e7e7ca96787a945d43bd8ff42f344f572b72 Mon Sep 17 00:00:00 2001 From: sidarth16 Date: Sat, 12 Jul 2025 17:03:57 +0530 Subject: [PATCH 1/6] added Function module to detect functions returning msg.sender directly or via alias --- slither/core/declarations/function.py | 78 +++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/slither/core/declarations/function.py b/slither/core/declarations/function.py index 09ccef7cd8..a6327dbbfb 100644 --- a/slither/core/declarations/function.py +++ b/slither/core/declarations/function.py @@ -217,6 +217,7 @@ def __init__(self, compilation_unit: "SlitherCompilationUnit") -> None: self._solidity_signature: Optional[str] = None self._signature_str: Optional[str] = None self._canonical_name: Optional[str] = None + self._is_returning_msg_sender: Optional[bool] = None self._is_protected: Optional[bool] = None self.compilation_unit: "SlitherCompilationUnit" = compilation_unit @@ -1528,6 +1529,83 @@ def get_summary( ) -> Tuple[str, str, str, List[str], List[str], List[str], List[str], List[str]]: pass + def is_returning_msg_sender(self) -> bool: + """ + Determine if the function returns `msg.sender` directly or through aliased address variables. + + This includes: + - Functions that explicitly returns `msg.sender` + - Functions that returns a variable which was directly or transitively assigned from `msg.sender` + + Covers: + - Direct returns: + ``` + return msg.sender + ``` + - Aliased returns like: + ``` + address a = msg.sender; + return a; + ``` + - Reassignments or address conversions: + ``` + address a = msg.sender; + address b = address(uint160(a)); + return b; + ``` + Does not cover : + - Returns via internal function calls, even if those functions return `msg.sender`: + ``` + function _getUser() internal view returns (address) { + return _msgSender(); // _msgSender() returns msg.sender + } + ``` + + Returns + (bool) + """ + from slither.core.solidity_types import ElementaryType + from slither.slithir.operations import Return, Assignment + + if self._is_returning_msg_sender is None: + + # Skip analysis if function doesn't return or doesn't return an address. + if not self.returns or not all(ret.type == ElementaryType('address') for ret in self.returns): + self._is_returning_msg_sender = False + return False + + return_vars = [] + assignment_map = {} + + for node in self.nodes: + for ir in node.irs: + # Direct return of msg.sender + if isinstance(ir, Return) : + ir_return_vars = [i.name for i in ir.values if hasattr(i, 'name')] + if 'msg.sender' in ir_return_vars: + self._is_returning_msg_sender = True + return True + return_vars.extend(ir_return_vars) + + # Track assignments where an address-typed variable is assigned from another variable. + # This helps trace msg.sender aliases through reassignments. + if isinstance(ir, Assignment) and ir.lvalue.type==ElementaryType('address'): + if hasattr(ir.lvalue, 'name') and hasattr(ir.rvalue, 'name'): + if ir.rvalue.name in assignment_map: + assignment_map[ir.lvalue.name] = assignment_map[ir.rvalue.name] + else: + assignment_map[ir.lvalue.name] = ir.rvalue.name + + for var in return_vars: + if var not in assignment_map: + continue + var = assignment_map[var] + if var == 'msg.sender': + self._is_returning_msg_sender = True + return True + self._is_returning_msg_sender = False + return self._is_returning_msg_sender + def is_protected(self) -> bool: """ Determine if the function is protected using a check on msg.sender From 15537d6ac51f3505c9c024affefea115ca8cdf45 Mon Sep 17 00:00:00 2001 From: sidarth16 Date: Sat, 12 Jul 2025 17:43:24 +0530 Subject: [PATCH 2/6] update docstring --- slither/core/declarations/function.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/slither/core/declarations/function.py b/slither/core/declarations/function.py index a6327dbbfb..82e83d6f8b 100644 --- a/slither/core/declarations/function.py +++ b/slither/core/declarations/function.py @@ -1547,12 +1547,7 @@ def is_returning_msg_sender(self) -> bool: address a = msg.sender; return a; ``` - - Reassignments or address conversions: - ``` - address a = msg.sender; - address b = address(uint160(a)); - return b; - ``` + Does not cover : - Returns via internal function calls, even if those functions return `msg.sender`: ``` From 64a64fbd8661c7a1d9d6b1df5f340d5a451cd687 Mon Sep 17 00:00:00 2001 From: sidarth16 Date: Sat, 12 Jul 2025 18:23:27 +0530 Subject: [PATCH 3/6] update black --- slither/core/declarations/function.py | 36 +++++++++++++++------------ 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/slither/core/declarations/function.py b/slither/core/declarations/function.py index 82e83d6f8b..cf4aba5e36 100644 --- a/slither/core/declarations/function.py +++ b/slither/core/declarations/function.py @@ -1538,18 +1538,18 @@ def is_returning_msg_sender(self) -> bool: - Functions that returns a variable which was directly or transitively assigned from `msg.sender` Covers: - - Direct returns: + - Direct returns: ``` return msg.sender ``` - - Aliased returns like: + - Aliased returns: ``` address a = msg.sender; return a; ``` - - Does not cover : - - Returns via internal function calls, even if those functions return `msg.sender`: + + Does not cover : + - Returns via internal function calls, even if those functions return `msg.sender`: ``` function _getUser() internal view returns (address) { return _msgSender(); // _msgSender() returns msg.sender @@ -1565,27 +1565,29 @@ def is_returning_msg_sender(self) -> bool: if self._is_returning_msg_sender is None: # Skip analysis if function doesn't return or doesn't return an address. - if not self.returns or not all(ret.type == ElementaryType('address') for ret in self.returns): + if not self.returns or not all( + ret.type == ElementaryType("address") for ret in self.returns + ): self._is_returning_msg_sender = False return False - + return_vars = [] assignment_map = {} for node in self.nodes: for ir in node.irs: # Direct return of msg.sender - if isinstance(ir, Return) : - ir_return_vars = [i.name for i in ir.values if hasattr(i, 'name')] - if 'msg.sender' in ir_return_vars: + if isinstance(ir, Return): + ir_return_vars = [i.name for i in ir.values if hasattr(i, "name")] + if "msg.sender" in ir_return_vars: self._is_returning_msg_sender = True return True return_vars.extend(ir_return_vars) - # Track assignments where an address-typed variable is assigned from another variable. + # Track assignments where an address-typed variable is assigned. # This helps trace msg.sender aliases through reassignments. - if isinstance(ir, Assignment) and ir.lvalue.type==ElementaryType('address'): - if hasattr(ir.lvalue, 'name') and hasattr(ir.rvalue, 'name'): + if isinstance(ir, Assignment) and ir.lvalue.type == ElementaryType("address"): + if hasattr(ir.lvalue, "name") and hasattr(ir.rvalue, "name"): if ir.rvalue.name in assignment_map: assignment_map[ir.lvalue.name] = assignment_map[ir.rvalue.name] else: @@ -1595,12 +1597,14 @@ def is_returning_msg_sender(self) -> bool: if var not in assignment_map: continue var = assignment_map[var] - if var == 'msg.sender': + if var == "msg.sender": self._is_returning_msg_sender = True - return True + return True + self._is_returning_msg_sender = False + return self._is_returning_msg_sender - + def is_protected(self) -> bool: """ Determine if the function is protected using a check on msg.sender From f17f5d384e4289cb64e2d56151517f2463a1d7da Mon Sep 17 00:00:00 2001 From: sidarth16 Date: Sat, 12 Jul 2025 18:48:22 +0530 Subject: [PATCH 4/6] update pylint --- slither/core/declarations/function.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/slither/core/declarations/function.py b/slither/core/declarations/function.py index cf4aba5e36..1e16f29cf8 100644 --- a/slither/core/declarations/function.py +++ b/slither/core/declarations/function.py @@ -1586,12 +1586,14 @@ def is_returning_msg_sender(self) -> bool: # Track assignments where an address-typed variable is assigned. # This helps trace msg.sender aliases through reassignments. - if isinstance(ir, Assignment) and ir.lvalue.type == ElementaryType("address"): - if hasattr(ir.lvalue, "name") and hasattr(ir.rvalue, "name"): - if ir.rvalue.name in assignment_map: - assignment_map[ir.lvalue.name] = assignment_map[ir.rvalue.name] - else: - assignment_map[ir.lvalue.name] = ir.rvalue.name + if ( + isinstance(ir, Assignment) and ir.lvalue.type == ElementaryType("address") + and hasattr(ir.lvalue, "name") and hasattr(ir.rvalue, "name") + ): + if ir.rvalue.name in assignment_map: + assignment_map[ir.lvalue.name] = assignment_map[ir.rvalue.name] + else: + assignment_map[ir.lvalue.name] = ir.rvalue.name for var in return_vars: if var not in assignment_map: From 5b230aac9b5f386b9c359926e1c40442e17acb5d Mon Sep 17 00:00:00 2001 From: sidarth16 Date: Sat, 12 Jul 2025 18:59:46 +0530 Subject: [PATCH 5/6] update black & optimized assignment_map logic --- slither/core/declarations/function.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/slither/core/declarations/function.py b/slither/core/declarations/function.py index 1e16f29cf8..db3f9d72d0 100644 --- a/slither/core/declarations/function.py +++ b/slither/core/declarations/function.py @@ -1587,13 +1587,17 @@ def is_returning_msg_sender(self) -> bool: # Track assignments where an address-typed variable is assigned. # This helps trace msg.sender aliases through reassignments. if ( - isinstance(ir, Assignment) and ir.lvalue.type == ElementaryType("address") - and hasattr(ir.lvalue, "name") and hasattr(ir.rvalue, "name") + isinstance(ir, Assignment) + and ir.lvalue.type == ElementaryType("address") + and hasattr(ir.lvalue, "name") + and hasattr(ir.rvalue, "name") ): - if ir.rvalue.name in assignment_map: - assignment_map[ir.lvalue.name] = assignment_map[ir.rvalue.name] - else: - assignment_map[ir.lvalue.name] = ir.rvalue.name + lval, rval = ir.lvalue.name, ir.rvalue.name + assignment_map[lval] = assignment_map.get(rval, rval) + # if ir.rvalue.name in assignment_map: + # assignment_map[ir.lvalue.name] = assignment_map[ir.rvalue.name] + # else: + # assignment_map[ir.lvalue.name] = ir.rvalue.name for var in return_vars: if var not in assignment_map: From addf2004956e7ff5261c429106a547f0e9f00a1d Mon Sep 17 00:00:00 2001 From: sidarth16 Date: Sat, 12 Jul 2025 19:22:08 +0530 Subject: [PATCH 6/6] remove redundant comments --- slither/core/declarations/function.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/slither/core/declarations/function.py b/slither/core/declarations/function.py index db3f9d72d0..d21f22b14c 100644 --- a/slither/core/declarations/function.py +++ b/slither/core/declarations/function.py @@ -1594,10 +1594,6 @@ def is_returning_msg_sender(self) -> bool: ): lval, rval = ir.lvalue.name, ir.rvalue.name assignment_map[lval] = assignment_map.get(rval, rval) - # if ir.rvalue.name in assignment_map: - # assignment_map[ir.lvalue.name] = assignment_map[ir.rvalue.name] - # else: - # assignment_map[ir.lvalue.name] = ir.rvalue.name for var in return_vars: if var not in assignment_map: