Skip to content

feat(lint): add UnsafeTypecast lint #11046

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

TropicalDog17
Copy link
Contributor

Motivation

Closes #11029

Solution

PR Checklist

  • Added Tests
  • Added Documentation
  • Breaking changes

@@ -353,7 +353,7 @@ jiff = "0.2"
heck = "0.5"
uuid = "1.17.0"
flate2 = "1.1"

num-bigint = "0.4"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed?

@@ -0,0 +1,106 @@
contract UnsafeTypecast {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current throws false positives when typecasting to the same type or upcasting. We need more granular tests, here's some tests that ensure uint/int downcasts always trigger and upcasts never trigger.

Suggested change
contract UnsafeTypecast {
contract UnsafeTypecast {
function upcastSafeUint() public pure {
uint8 a = type(uint8).max;
uint16 b = uint16(a);
uint24 c = uint24(b);
uint32 d = uint32(c);
uint40 e = uint40(d);
uint48 f = uint48(e);
uint56 g = uint56(f);
uint64 h = uint64(g);
uint72 i = uint72(h);
uint80 j = uint80(i);
uint88 k = uint88(j);
uint96 l = uint96(k);
uint104 m = uint104(l);
uint112 n = uint112(m);
uint120 o = uint120(n);
uint128 p = uint128(o);
uint136 q = uint136(p);
uint144 r = uint144(q);
uint152 s = uint152(r);
uint160 t = uint160(s);
uint168 u = uint168(t);
uint176 v = uint176(u);
uint184 w = uint184(v);
uint192 x = uint192(w);
uint200 y = uint200(x);
uint208 z = uint208(y);
uint216 A = uint216(z);
uint224 B = uint224(A);
uint232 C = uint232(B);
uint240 D = uint240(C);
uint248 E = uint248(D);
uint256 F = uint256(E);
}
function upcastSafeInt() public pure {
int8 a = type(int8).max;
int16 b = int16(a);
int24 c = int24(b);
int32 d = int32(c);
int40 e = int40(d);
int48 f = int48(e);
int56 g = int56(f);
int64 h = int64(g);
int72 i = int72(h);
int80 j = int80(i);
int88 k = int88(j);
int96 l = int96(k);
int104 m = int104(l);
int112 n = int112(m);
int120 o = int120(n);
int128 p = int128(o);
int136 q = int136(p);
int144 r = int144(q);
int152 s = int152(r);
int160 t = int160(s);
int168 u = int168(t);
int176 v = int176(u);
int184 w = int184(v);
int192 x = int192(w);
int200 y = int200(x);
int208 z = int208(y);
int216 A = int216(z);
int224 B = int224(A);
int232 C = int232(B);
int240 D = int240(C);
int248 E = int248(D);
int256 F = int256(E);
}
function downcastSafeUint() public pure {
uint256 a = type(uint256).max;
uint248 b = uint248(a); //~WARN: typecasts that can truncate values should be avoided
uint240 c = uint240(b); //~WARN: typecasts that can truncate values should be avoided
uint232 d = uint232(c); //~WARN: typecasts that can truncate values should be avoided
uint224 e = uint224(d); //~WARN: typecasts that can truncate values should be avoided
uint216 f = uint216(e); //~WARN: typecasts that can truncate values should be avoided
uint208 g = uint208(f); //~WARN: typecasts that can truncate values should be avoided
uint200 h = uint200(g); //~WARN: typecasts that can truncate values should be avoided
uint192 i = uint192(h); //~WARN: typecasts that can truncate values should be avoided
uint184 j = uint184(i); //~WARN: typecasts that can truncate values should be avoided
uint176 k = uint176(j); //~WARN: typecasts that can truncate values should be avoided
uint168 l = uint168(k); //~WARN: typecasts that can truncate values should be avoided
uint160 m = uint160(l); //~WARN: typecasts that can truncate values should be avoided
uint152 n = uint152(m); //~WARN: typecasts that can truncate values should be avoided
uint144 o = uint144(n); //~WARN: typecasts that can truncate values should be avoided
uint136 p = uint136(o); //~WARN: typecasts that can truncate values should be avoided
uint128 q = uint128(p); //~WARN: typecasts that can truncate values should be avoided
uint120 r = uint120(q); //~WARN: typecasts that can truncate values should be avoided
uint112 s = uint112(r); //~WARN: typecasts that can truncate values should be avoided
uint104 t = uint104(s); //~WARN: typecasts that can truncate values should be avoided
uint96 u = uint96(t); //~WARN: typecasts that can truncate values should be avoided
uint88 v = uint88(u); //~WARN: typecasts that can truncate values should be avoided
uint80 w = uint80(v); //~WARN: typecasts that can truncate values should be avoided
uint72 x = uint72(w); //~WARN: typecasts that can truncate values should be avoided
uint64 y = uint64(x); //~WARN: typecasts that can truncate values should be avoided
uint56 z = uint56(y); //~WARN: typecasts that can truncate values should be avoided
uint48 A = uint48(z); //~WARN: typecasts that can truncate values should be avoided
uint40 B = uint40(A); //~WARN: typecasts that can truncate values should be avoided
uint32 C = uint32(B); //~WARN: typecasts that can truncate values should be avoided
uint24 D = uint24(C); //~WARN: typecasts that can truncate values should be avoided
uint16 E = uint16(D); //~WARN: typecasts that can truncate values should be avoided
uint8 F = uint8(E); //~WARN: typecasts that can truncate values should be avoided
}
function downcastSafeInt() public pure {
int256 a = type(int256).max;
int248 b = int248(a); //~WARN: typecasts that can truncate values should be avoided
int240 c = int240(b); //~WARN: typecasts that can truncate values should be avoided
int232 d = int232(c); //~WARN: typecasts that can truncate values should be avoided
int224 e = int224(d); //~WARN: typecasts that can truncate values should be avoided
int216 f = int216(e); //~WARN: typecasts that can truncate values should be avoided
int208 g = int208(f); //~WARN: typecasts that can truncate values should be avoided
int200 h = int200(g); //~WARN: typecasts that can truncate values should be avoided
int192 i = int192(h); //~WARN: typecasts that can truncate values should be avoided
int184 j = int184(i); //~WARN: typecasts that can truncate values should be avoided
int176 k = int176(j); //~WARN: typecasts that can truncate values should be avoided
int168 l = int168(k); //~WARN: typecasts that can truncate values should be avoided
int160 m = int160(l); //~WARN: typecasts that can truncate values should be avoided
int152 n = int152(m); //~WARN: typecasts that can truncate values should be avoided
int144 o = int144(n); //~WARN: typecasts that can truncate values should be avoided
int136 p = int136(o); //~WARN: typecasts that can truncate values should be avoided
int128 q = int128(p); //~WARN: typecasts that can truncate values should be avoided
int120 r = int120(q); //~WARN: typecasts that can truncate values should be avoided
int112 s = int112(r); //~WARN: typecasts that can truncate values should be avoided
int104 t = int104(s); //~WARN: typecasts that can truncate values should be avoided
int96 u = int96(t); //~WARN: typecasts that can truncate values should be avoided
int88 v = int88(u); //~WARN: typecasts that can truncate values should be avoided
int80 w = int80(v); //~WARN: typecasts that can truncate values should be avoided
int72 x = int72(w); //~WARN: typecasts that can truncate values should be avoided
int64 y = int64(x); //~WARN: typecasts that can truncate values should be avoided
int56 z = int56(y); //~WARN: typecasts that can truncate values should be avoided
int48 A = int48(z); //~WARN: typecasts that can truncate values should be avoided
int40 B = int40(A); //~WARN: typecasts that can truncate values should be avoided
int32 C = int32(B); //~WARN: typecasts that can truncate values should be avoided
int24 D = int24(C); //~WARN: typecasts that can truncate values should be avoided
int16 E = int16(D); //~WARN: typecasts that can truncate values should be avoided
int8 F = int8(E); //~WARN: typecasts that can truncate values should be avoided
}
}

warning[unsafe-typecast]: typecasts that can truncate values should be avoided
--> ROOT/testdata/UnsafeTypecast.sol:LL:CC
|
5 | uint128 b = uint128(a);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uint128 -> uint128 is safe
uintN -> uintN is always safe
intN -> intN is always safe

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a is an address type so i think it still unsafe

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely ensure the compiler isn't catching some of these, cannot directly convert an address to uint128 without a compile error.

warning[unsafe-typecast]: typecasts that can truncate values should be avoided
--> ROOT/testdata/UnsafeTypecast.sol:LL:CC
|
94 | uint128 a = uint128(1000000000000000000000000000000000000000);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure the compiler prevents you from downcasting a literal that's smaller than the target type size.

Comment on lines +9 to +15
warning[unsafe-typecast]: typecasts that can truncate values should be avoided
--> ROOT/testdata/UnsafeTypecast.sol:LL:CC
|
8 | uint64 c = uint64(a);
| ---------
|
= help: https://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast
Copy link

@0xClandestine 0xClandestine Jul 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to emit a suggested fix as outlined in #11046

Can checkout my UnwrappedModifierLogic lint for reference #10967

@@ -0,0 +1,106 @@
contract UnsafeTypecast {
function numericDowncasts() public {
Copy link

@0xClandestine 0xClandestine Jul 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will also want a test for uint to int same size (unsafe since integers have leading sign bit).

Also a test for int to uint same size (safe since type(uintN).max > type(intN).max)

Suggested change
function numericDowncasts() public {
function numericDowncasts() public {
function unsafeUintToIntSameSize() public pure {
uint256 a = type(uint256).max;
int256 b = int256(a); //~WARN: typecasts that can truncate values should be avoided
uint248 c = type(uint248).max;
int248 d = int248(c); //~WARN: typecasts that can truncate values should be avoided
uint240 e = type(uint240).max;
int240 f = int240(e); //~WARN: typecasts that can truncate values should be avoided
uint232 g = type(uint232).max;
int232 h = int232(g); //~WARN: typecasts that can truncate values should be avoided
uint224 i = type(uint224).max;
int224 j = int224(i); //~WARN: typecasts that can truncate values should be avoided
uint216 k = type(uint216).max;
int216 l = int216(k); //~WARN: typecasts that can truncate values should be avoided
uint208 m = type(uint208).max;
int208 n = int208(m); //~WARN: typecasts that can truncate values should be avoided
uint200 o = type(uint200).max;
int200 p = int200(o); //~WARN: typecasts that can truncate values should be avoided
uint192 q = type(uint192).max;
int192 r = int192(q); //~WARN: typecasts that can truncate values should be avoided
uint184 s = type(uint184).max;
int184 t = int184(s); //~WARN: typecasts that can truncate values should be avoided
uint176 u = type(uint176).max;
int176 v = int176(u); //~WARN: typecasts that can truncate values should be avoided
uint168 w = type(uint168).max;
int168 x = int168(w); //~WARN: typecasts that can truncate values should be avoided
uint160 y = type(uint160).max;
int160 z = int160(y); //~WARN: typecasts that can truncate values should be avoided
uint152 A = type(uint152).max;
int152 B = int152(A); //~WARN: typecasts that can truncate values should be avoided
uint144 C = type(uint144).max;
int144 D = int144(C); //~WARN: typecasts that can truncate values should be avoided
uint136 E = type(uint136).max;
int136 F = int136(E); //~WARN: typecasts that can truncate values should be avoided
uint128 G = type(uint128).max;
int128 H = int128(G); //~WARN: typecasts that can truncate values should be avoided
uint120 I = type(uint120).max;
int120 J = int120(I); //~WARN: typecasts that can truncate values should be avoided
uint112 K = type(uint112).max;
int112 L = int112(K); //~WARN: typecasts that can truncate values should be avoided
uint104 M = type(uint104).max;
int104 N = int104(M); //~WARN: typecasts that can truncate values should be avoided
uint96 O = type(uint96).max;
int96 P = int96(O); //~WARN: typecasts that can truncate values should be avoided
uint88 Q = type(uint88).max;
int88 R = int88(Q); //~WARN: typecasts that can truncate values should be avoided
uint80 S = type(uint80).max;
int80 T = int80(S); //~WARN: typecasts that can truncate values should be avoided
uint72 U = type(uint72).max;
int72 V = int72(U); //~WARN: typecasts that can truncate values should be avoided
uint64 W = type(uint64).max;
int64 X = int64(W); //~WARN: typecasts that can truncate values should be avoided
uint56 Y = type(uint56).max;
int56 Z = int56(Y); //~WARN: typecasts that can truncate values should be avoided
uint48 a1 = type(uint48).max;
int48 b1 = int48(a1); //~WARN: typecasts that can truncate values should be avoided
uint40 c1 = type(uint40).max;
int40 d1 = int40(c1); //~WARN: typecasts that can truncate values should be avoided
uint32 e1 = type(uint32).max;
int32 f1 = int32(e1); //~WARN: typecasts that can truncate values should be avoided
uint24 g1 = type(uint24).max;
int24 h1 = int24(g1); //~WARN: typecasts that can truncate values should be avoided
uint16 i1 = type(uint16).max;
int16 j1 = int16(i1); //~WARN: typecasts that can truncate values should be avoided
uint8 k1 = type(uint8).max;
int8 l1 = int8(k1); //~WARN: typecasts that can truncate values should be avoided
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

feat(forge-lint): add UnsafeTypecast lint
2 participants