Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions libsolidity/analysis/TypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
bool TypeChecker::visit(IfStatement const& _ifStatement)
{
expectBoolOrShieldedBool(_ifStatement.condition());
checkAndWarnShieldedCondition(_ifStatement.condition());
_ifStatement.trueStatement().accept(*this);
if (_ifStatement.falseStatement())
_ifStatement.falseStatement()->accept(*this);
Expand Down Expand Up @@ -1125,6 +1126,7 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement)
bool TypeChecker::visit(WhileStatement const& _whileStatement)
{
expectBoolOrShieldedBool(_whileStatement.condition());
checkAndWarnShieldedCondition(_whileStatement.condition());
_whileStatement.body().accept(*this);
return false;
}
Expand All @@ -1134,7 +1136,10 @@ bool TypeChecker::visit(ForStatement const& _forStatement)
if (_forStatement.initializationExpression())
_forStatement.initializationExpression()->accept(*this);
if (_forStatement.condition())
{
expectBoolOrShieldedBool(*_forStatement.condition());
checkAndWarnShieldedCondition(*_forStatement.condition());
}
if (_forStatement.loopExpression())
_forStatement.loopExpression()->accept(*this);
_forStatement.body().accept(*this);
Expand Down Expand Up @@ -1413,6 +1418,7 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement)
bool TypeChecker::visit(Conditional const& _conditional)
{
expectBoolOrShieldedBool(_conditional.condition());
checkAndWarnShieldedCondition(_conditional.condition());

_conditional.trueExpression().accept(*this);
_conditional.falseExpression().accept(*this);
Expand Down Expand Up @@ -2215,6 +2221,21 @@ void TypeChecker::typeCheckABIEncodeFunctions(
{
auto const& argType = type(*arguments[i]);

// Prevent encoding of shielded types (skip check for tuples to avoid unimplemented feature errors)
if (argType && argType->category() != Type::Category::Tuple)
{
if (argType->isShielded() || argType->containsShieldedType())
{
m_errorReporter.typeError(
9664_error,
arguments[i]->location(),
"Shielded types cannot be ABI-encoded. "
"Encoding shielded values would leak information through the resulting bytes."
);
continue;
}
}

if (argType->category() == Type::Category::RationalNumber)
{
auto const& rationalType = dynamic_cast<RationalNumberType const&>(*argType);
Expand Down Expand Up @@ -2354,6 +2375,22 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
return;
}
solAssert(!externalFunctionType->takesArbitraryParameters(), "Function must have fixed parameters.");

// Check if function has shielded parameters
for (auto const& paramType : externalFunctionType->parameterTypes())
{
if (paramType && (paramType->isShielded() || paramType->containsShieldedType()))
{
m_errorReporter.typeError(
9664_error,
arguments[0]->location(),
"Cannot use abi.encodeCall with functions that have shielded parameter types. "
"Encoding shielded values would leak information through the resulting bytes."
);
return;
}
}

// Tuples with only one component become that component
std::vector<ASTPointer<Expression const>> callArguments;

Expand Down Expand Up @@ -2403,6 +2440,7 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
for (size_t i = 0; i < numParameters; i++)
{
Type const& argType = *type(*callArguments[i]);

BoolResult result = argType.isImplicitlyConvertibleTo(*externalFunctionType->parameterTypes()[i]);
if (!result)
m_errorReporter.typeError(
Expand Down Expand Up @@ -4268,6 +4306,37 @@ bool TypeChecker::expectBoolOrShieldedBool(Expression const& _expression) {
return expectType(_expression, *TypeProvider::boolean());
}

bool TypeChecker::checkAndWarnShieldedCondition(Expression const& _condition)
{
Type const* condType = type(_condition);

// Check if condition result type is shielded
bool isShielded = condType && (condType->isShielded() || condType->containsShieldedType());

// For binary operations, also check if operands are shielded (e.g., suint >= suint produces bool, not sbool)
if (!isShielded)
{
if (auto binOp = dynamic_cast<BinaryOperation const*>(&_condition))
{
Type const* leftType = type(binOp->leftExpression());
Type const* rightType = type(binOp->rightExpression());
isShielded = (leftType && (leftType->isShielded() || leftType->containsShieldedType())) ||
(rightType && (rightType->isShielded() || rightType->containsShieldedType()));
}
}

if (isShielded)
{
m_errorReporter.warning(
9663_error,
_condition.location(),
"Using shielded types in branching conditions can leak information through "
"observable execution patterns such as gas costs, state changes, and execution traces."
);
return true;
}
return false;
}

void TypeChecker::requireLValue(Expression const& _expression)
{
Expand Down
2 changes: 2 additions & 0 deletions libsolidity/analysis/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ class TypeChecker: private ASTConstVisitor
bool expectType(Expression const& _expression, Type const& _expectedType);
/// Helper function for conditionals that checks for either bool or shielded_bools.
bool expectBoolOrShieldedBool(Expression const& _expression);
/// Checks if a condition uses shielded types and emits a warning about potential information leakage.
bool checkAndWarnShieldedCondition(Expression const& _condition);
/// Runs type checks on @a _expression to infer its type and then checks that it is an LValue.
void requireLValue(Expression const& _expression);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ contract AES {
bytes32 public constant AES_KEY = hex"00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff";
bytes public constant PLAINTEXT = hex"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f";

suint96 NONCE;
uint96 NONCE;
bytes public encryptedData;
bytes public decryptedData;

/// @notice Generates a random nonce using seismicRng.
function updateNonce() public {
bytes32 rngOutput = seismicRng();
NONCE = suint96(suint256(rngOutput));
NONCE = uint96(uint256(rngOutput));
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ contract testContract {
}
}
// ----
// TypeError 5407: (172-183): Cannot implicitly convert component at position 0 from "saddress" to "saddress payable".
// TypeError 9664: (155-170): Cannot use abi.encodeCall with functions that have shielded parameter types. Encoding shielded values would leak information through the resulting bytes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
contract TestShieldedABIEncode {
function testEncode(suint value) public pure returns (bytes memory) {
return abi.encode(value);
}

function testEncodePacked(suint value) public pure returns (bytes memory) {
return abi.encodePacked(value);
}

function testEncodeWithSelector(suint value) public pure returns (bytes memory) {
return abi.encodeWithSelector(bytes4(0x12345678), value);
}

function testEncodeWithSignature(suint value) public pure returns (bytes memory) {
return abi.encodeWithSignature("transfer(uint256)", value);
}

function testEncodeCall(suint value) public pure returns (bytes memory) {
return abi.encodeCall(this.someFunction, (value));
}

function someFunction(suint) external {}

function testEncodeRegular(uint value) public pure returns (bytes memory) {
return abi.encode(value);
}
}
// ----
// TypeError 9664: (133-138): Shielded types cannot be ABI-encoded. Encoding shielded values would leak information through the resulting bytes.
// TypeError 9664: (260-265): Shielded types cannot be ABI-encoded. Encoding shielded values would leak information through the resulting bytes.
// TypeError 9664: (419-424): Shielded types cannot be ABI-encoded. Encoding shielded values would leak information through the resulting bytes.
// TypeError 9664: (581-586): Shielded types cannot be ABI-encoded. Encoding shielded values would leak information through the resulting bytes.
// TypeError 9664: (704-721): Cannot use abi.encodeCall with functions that have shielded parameter types. Encoding shielded values would leak information through the resulting bytes.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ contract TestSbool {
}

// ----
// Warning 9663: (81-82): Using shielded types in branching conditions can leak information through observable execution patterns such as gas costs, state changes, and execution traces.
// Warning 9663: (227-228): Using shielded types in branching conditions can leak information through observable execution patterns such as gas costs, state changes, and execution traces.
// Warning 9661: (294-315): Bool Literals converted to shielded bools will leak during contract deployment.
// Warning 9663: (332-333): Using shielded types in branching conditions can leak information through observable execution patterns such as gas costs, state changes, and execution traces.
// Warning 2018: (25-162): Function state mutability can be restricted to pure
// Warning 2018: (168-243): Function state mutability can be restricted to pure
// Warning 2018: (249-394): Function state mutability can be restricted to pure
3 changes: 3 additions & 0 deletions test/libsolidity/syntaxTests/parsing/shielded_for_loop.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ contract TestForSbool {
return uint(counter);
}
}
// ----
// Warning 9663: (128-137): Using shielded types in branching conditions can leak information through observable execution patterns such as gas costs, state changes, and execution traces.
// Warning 9663: (182-198): Using shielded types in branching conditions can leak information through observable execution patterns such as gas costs, state changes, and execution traces.

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ contract test {
}
// ----
// Warning 9660: (62-85): Literals converted to shielded integers will leak during contract deployment.
// Warning 9663: (103-116): Using shielded types in branching conditions can leak information through observable execution patterns such as gas costs, state changes, and execution traces.
// Warning 5740: (118-121): Unreachable code.
// Warning 5740: (160-168): Unreachable code.
// Warning 5667: (33-43): Unused function parameter. Remove or comment out the variable name to silence this warning.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ contract test {
}
// ----
// Warning 9660: (62-83): Literals converted to shielded integers will leak during contract deployment.
// Warning 9663: (112-125): Using shielded types in branching conditions can leak information through observable execution patterns such as gas costs, state changes, and execution traces.
// Warning 5667: (33-43): Unused function parameter. Remove or comment out the variable name to silence this warning.
// Warning 2018: (20-159): Function state mutability can be restricted to pure
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ contract test {
}
// ----
// Warning 9660: (67-88): Literals converted to shielded integers will leak during contract deployment.
// Warning 9663: (90-103): Using shielded types in branching conditions can leak information through observable execution patterns such as gas costs, state changes, and execution traces.
// Warning 5740: (105-108): Unreachable code.
// Warning 5740: (147-155): Unreachable code.
// Warning 5667: (33-43): Unused function parameter. Remove or comment out the variable name to silence this warning.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ contract test {
}
}
// ----
// Warning 9663: (81-94): Using shielded types in branching conditions can leak information through observable execution patterns such as gas costs, state changes, and execution traces.
// Warning 9660: (117-135): Literals converted to shielded integers will leak during contract deployment.
// Warning 6321: (61-65): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.
// Warning 2072: (117-124): Unused local variable.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
contract TestShieldedConditionalWarnings {
function testShieldedBool(sbool condition) public pure returns (uint) {
return condition ? 1 : 2;
}

function testShieldedComparison(suint a, suint b) public pure returns (uint) {
return a >= b ? 100 : 50;
}

function testRegularBool(bool flag) public pure returns (uint) {
return flag ? 1 : 2;
}

function testRegularComparison(uint a, uint b) public pure returns (uint) {
return a >= b ? 100 : 50;
}
}
// ----
// Warning 9663: (134-143): Using shielded types in branching conditions can leak information through observable execution patterns such as gas costs, state changes, and execution traces.
// Warning 9663: (258-264): Using shielded types in branching conditions can leak information through observable execution patterns such as gas costs, state changes, and execution traces.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
contract TestShieldedForWarnings {
function testShieldedBool(sbool condition) public {
for (; condition;) {
break;
}
}

function testShieldedComparison(suint counter, suint limit) public {
for (; counter < limit;) {
counter++;
}
}

function testRegularBool(bool flag) public {
for (; flag;) {
break;
}
}

function testRegularComparison(uint counter, uint limit) public {
for (; counter < limit;) {
counter++;
}
}
}
// ----
// Warning 9663: (106-115): Using shielded types in branching conditions can leak information through observable execution patterns such as gas costs, state changes, and execution traces.
// Warning 9663: (244-259): Using shielded types in branching conditions can leak information through observable execution patterns such as gas costs, state changes, and execution traces.
// Warning 2018: (39-154): Function state mutability can be restricted to pure
// Warning 2018: (160-302): Function state mutability can be restricted to pure
// Warning 2018: (308-411): Function state mutability can be restricted to pure
// Warning 2018: (417-556): Function state mutability can be restricted to pure
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
contract TestShieldedIfWarnings {
function testShieldedBool(sbool condition) public {
if (condition) {
uint x = 1;
}
}

function testShieldedComparison(suint a, suint b) public {
if (a >= b) {
uint x = 1;
}
}

function testRegularBool(bool flag) public {
if (flag) {
uint x = 1;
}
}

function testRegularComparison(uint a, uint b) public {
if (a >= b) {
uint x = 1;
}
}
}
// ----
// Warning 9663: (102-111): Using shielded types in branching conditions can leak information through observable execution patterns such as gas costs, state changes, and execution traces.
// Warning 9663: (231-237): Using shielded types in branching conditions can leak information through observable execution patterns such as gas costs, state changes, and execution traces.
// Warning 2072: (127-133): Unused local variable.
// Warning 2072: (253-259): Unused local variable.
// Warning 2072: (363-369): Unused local variable.
// Warning 2072: (486-492): Unused local variable.
// Warning 2018: (38-154): Function state mutability can be restricted to pure
// Warning 2018: (160-280): Function state mutability can be restricted to pure
// Warning 2018: (286-390): Function state mutability can be restricted to pure
// Warning 2018: (396-513): Function state mutability can be restricted to pure
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
contract TestShieldedWhileWarnings {
function testShieldedBool(sbool condition) public {
while (condition) {
break;
}
}

function testShieldedComparison(suint counter, suint limit) public {
while (counter < limit) {
counter++;
}
}

function testRegularBool(bool flag) public {
while (flag) {
break;
}
}

function testRegularComparison(uint counter, uint limit) public {
while (counter < limit) {
counter++;
}
}
}
// ----
// Warning 9663: (108-117): Using shielded types in branching conditions can leak information through observable execution patterns such as gas costs, state changes, and execution traces.
// Warning 9663: (245-260): Using shielded types in branching conditions can leak information through observable execution patterns such as gas costs, state changes, and execution traces.
// Warning 2018: (41-155): Function state mutability can be restricted to pure
// Warning 2018: (161-302): Function state mutability can be restricted to pure
// Warning 2018: (308-410): Function state mutability can be restricted to pure
// Warning 2018: (416-554): Function state mutability can be restricted to pure