Как разработчик Solidity, ориентированный на безопасность, это ваш знак быть очень осторожным при использовании unchecked блоков и встроенной ассемблерной программы. С версии 0.8.0 и позже компилятор автоматически обрабатывает переполнения/недостатки. Но что происходит, если переменная, разрешенная к переполнению, затем используется в yul? EVM на самом деле не имеет концепции узких типов, поэтому низкоуровневый код yul работает с полным 256-битным размером слова. Вы уже видите, к чему это ведет? В этом сильно упрощенном сценарии, с которым я столкнулся в своей текущей работе с @CyfrinAudits, переменная длины тихо переполняет uint8 в unchecked блоке и оборачивается до 254, как и ожидалось. Однако, когда она используется для изменения размера массива в последующем блоке встроенной ассемблерной программы, грязные верхние биты сохраняются, как будто переполнение никогда не происходило! Это приводит к тому, что длина массива оказывается значительно больше ожидаемой, даже несмотря на то, что мы могли бы ожидать, что она будет ограничена по модулю максимального значения, представимого переменной длины. Используя отладчик foundry и cast --to-base <0xfe|0x01fe> dec, мы можем подтвердить, что наши предположения были неверными, и верхние биты действительно остаются грязными при записи длины в память. Хотя этот пример можно упростить еще больше, ключевой вывод заключается в том, чтобы всегда быть очень подозрительным к unchecked/assembly блокам и учитывать, как верхние биты узких типов могут стать грязными, даже для чего-то столь простого в yul, как присваивание. Иногда это может показаться эзотерической ошибкой, которую невозможно заметить без глубоких знаний о внутреннем устройстве EVM и компилятора Solidity, поэтому, осознав, что происходит, я нашел этот конкретный пример довольно полезным. Оставьте комментарий ниже, если у вас есть какие-либо вопросы, но, надеюсь, вы тоже нашли это полезным!
7,76K