Memory access out of bounds как исправить
Перейти к содержимому

Memory access out of bounds как исправить

  • автор:

Memory Access Out of Bounds — WebGL

I was getting this error when I try to load my Web game: enter image description here

My game is loaded at this domain: Highest Flavor Website Link

This is my project settings at the time of build export: enter image description here

Please give me some suggestion to solve this problem.

Siddharth's user avatar

3 Answers 3

This error happened to me due to using the dynamic keyword. After replacing all my dynamic types with object the error was gone. Apparently the dynamic keyword is not supported on WebGL builds.

I’ve finally fixed the same error in my project. Here is what i did:

Narrow down the line of code that crashes WebGL. Go through the painfull process of pinpointing the line of code that is the source of the error. I had this "luck" that error occured when I’ve hit button and tried to load UIScene in Addition mode. So at first I found the script in the scene which when disabled got rid of the crash. Then I’ve repeated the steps and digged deeper into lines. After 2 hours of builds, comments etc I’ve found that the code was not working was setting the new color for UnityEngine.UI.Image. Which was pretty weird because in playmode everything worked ok.

Find solution. I think my solution might not be universal but I think there is something going in the Unity gameloop/lifecycle when running WebGL. The source of the problem was that i set the field that was storing my UI.Image in the Start method and then in some function I’ve tried to change the color.
Property was not exposed in inspector.

then this line below was the cause of the crash

  1. Fixing — so I’ve added [SerializeField] attribute to the field and set it up in editor, dragged the Image into the inspector slot. An it fixed it. I suspect that WebGL build might perform some methods in different order, or maybe the multipleScene loaded together might to do with it. I’m not sure but the field was not set properly with previous code fot WebGL build.

Bonus: I’ve also fixed some issues that was not critical (not crushing WebGL game) but not working properly as expected in playmode. This also has got to do with trying to set some object in Start() method and then doing something on them. The collection was not set up (probably null) in WebGL.

And this also started working when I’ve added [SerializeField] to effects and hook it up in the scene.

Использование подсказок, включаемых в исходный код, помогающих GCC выявлять случаи переполнения буфера

Ошибки, связанные с доступом к областям памяти, которые находятся за пределами допустимого адресного пространства (out-of-bounds memory access), в 2021 году всё ещё пребывают в списке самых опасных уязвимостей ПО CWE Top 25. Известно, что ошибочные операции записи данных (out-of-bounds write, CWE-787) с двенадцатого места, которое они занимали в 2019 году, перешли в 2020 году на второе. А неправильные операции чтения данных (out-of-bounds read, CWE-125) в тех же временных пределах сменили пятое место на четвёртое.

Понимание важности раннего выявления ошибок, приводящих к вышеозначенным проблемам, привело к тому, что в свежих релизах компиляторов GNU Compiler Collection (GCC) была значительно улучшена возможность детектирования подобных ошибок. Речь идёт об использовании ключей для проведения проверок и вывода предупреждений наподобие -Warray-bounds, -Wformat-overflow, -Wstringop-overflow и (самая свежая возможность, появившаяся в GCC 11) -Wstringop-overread. Но всем этим проверкам свойственно одно и то же ограничение, связанное с тем, что система может обнаруживать проблемные ситуации лишь в пределах отдельных функций. Получается, что, за исключением анализа небольшого набора встроенных в компилятор функций, вроде memcpy() , проверка прекращается на границе вызова функции. То есть, например, если буфер, объявленный в функции A, переполняется в функции B, вызванной из функции A, компилятор, если функция B не встроена в функцию A, на эту проблему не реагирует.

В этом материале речь пойдёт о трёх видах простых подсказок, применяемых на уровне исходного кода, которые программист может использовать для того чтобы помочь GCC выявлять операции, связанные с доступом к областям памяти, находящимся за пределами допустимого адресного пространства. Причём, эти подсказки помогают компилятору находить проблемы и при пересечении границ вызова функций, и даже тогда, когда функции определены в разных файлах с исходным кодом.

А именно, речь идёт о следующих возможностях:

  • Атрибут функций access (он появился в GCC 10, доступен и в C, и в C++).
  • Параметры функций, представленные массивами переменной длины (Variable-length array, VLA) (это — новая возможность, которая появилась в GCC 11, доступна она лишь в C).
  • Параметры функций, представленные массивами (новая возможность GCC 11, она доступна лишь в C).

Атрибут access

Применение атрибута access может оказаться полезным в функциях, которые, в виде одного из аргументов, принимают указатель на буфер, а в виде второго аргумента — размер буфера. Это, например, пара POSIX-функций read() и write() . Помимо того, что данный атрибут помогает программисту связать эти два параметра, он ещё и позволяет описать то, как именно функция работает с буфером. Атрибут применяется к объявлениям функций. Он используется и в местах вызова функций, и при анализе объявлений функций с целью выявления ошибочных операций доступа к памяти.

Этот атрибут имеет следующий синтаксис:

  • access (access-mode, ref-index)
  • access (access-mode, ref-index, size-index)

Аргумент access-mode описывает то, как именно функция будет работать с буфером, режим доступа к нему. В GCC 11 предусмотрено четыре режима доступа к буферам:

  • Режим read_only указывает на то, что функция лишь считывает данные из предоставленного ей буфера, но ничего в него не записывает. Ожидается, что буфер будет инициализирован источником вызова функции. Режим read_only обеспечивает более надёжные гарантии неизменности буфера, чем квалификатор const , использованный при объявлении буфера, так как квалификатор можно снять и буфер может быть модифицирован в корректной программе, если только сам объект буфера не является неизменным. Параметр, к которому применяется режим read_only , может быть объявлен с квалификатором const (но это необязательно). В C99 объявление параметра как read_only имеет тот же смысл, что объявление его с использованием одновременно и const , и restrict (правда, GCC 11 не распознаёт эти два варианта объявления параметров как равнозначные).
  • Режим write_only указывает на то, что функция осуществляет только запись данных в предоставленный ей буфер, но ничего из него не читает. Буфер может быть неинициализированным. Попытка применить режим write_only к const-параметру приводит к выдаче предупреждения и к игнорированию атрибута. Это, фактически, режим доступа, используемый по умолчанию для параметров, которым не назначен атрибут access .
  • Режим read_write указывает на то, что функция и читает данные из буфера, и пишет их в него. Ожидается, что функции будет предоставлен инициализированный буфер. Попытка применения режима доступа read_write к параметру, объявленному с квалификатором const , приведёт к выдаче предупреждения, а атрибут будет проигнорирован.
  • Режим none говорит о том, что функция никак не работает с буфером. Буфер может быть неинициализированным. Это — режим, который появился в GCC 11, он предназначен для функций, которые выполняют валидацию аргументов без доступа к данным, хранящимся в буфере.

Функция read() сохраняет данные в предоставленный ей буфер. Поэтому в качестве режима доступа используется write_only . Функция write() читает данные из буфера. Поэтому тут используется режим доступа read_only .

Атрибут access играет роль, подобную той, которую играет объявление параметра функции с использованием массива переменной длины, но использование атрибута даёт больший уровень гибкости. Помимо возможности указания режима доступа к буферу, аргумент size-index может быть связан с указателем, размер которого идёт в списке аргументов функции после него. Именно так часто и происходит. Массивам переменной длины посвящён следующий раздел.

Параметры функций, представленные массивами переменной длины

В C (но, в GCC 11, не в C++), параметр функции, объявленный в виде массива, может ссылаться на неконстантное выражение, включая предыдущие параметры той же функции, используемое для задания границ массива. Когда значение, представляющее границу, является ссылкой на другой параметр функции, объявление этого параметра должно предшествовать VLA (GCC содержит расширение, позволяющее обойти это ограничение языка; дополнительные сведения об этом можно найти в соответствующем разделе документации по GCC). Если подобным способом устанавливается лишь верхняя граница массива, то он становится обычным указателем, похожим на любой другой массив. В противном случае это — VLA. Так как это различие между двумя видами массивов в данном контексте является достаточно тонким, в диагностических сообщениях GCC все эти массивы называются VLA. Мы, в продолжение этого материала, тоже будем придерживаться такого вот упрощённого подхода к массивам. Например:

Эта функция принимает обычный массив (или, если точнее, указатель) в качестве второго аргумента. Количество элементов этого массива задаёт первый аргумент. Хотя это, в общем-то, не нарушает требований языка, передача функции массива с меньшим числом элементов, чем указано в первом аргументе, почти наверняка указывает на ошибку. GCC проверяет вызовы подобных функций и выдаёт предупреждения в тех случаях, когда определяет, что переданные им массивы меньше, чем ожидается. Например, имеется следующая программа:

При вызове функции init_array GCC находит проблему, выдавая следующее предупреждение:

Компилятор, выдавая это предупреждение, предполагает наличие ошибки в коде, на которую указывает то, что init_array передают массив, который меньше, чем указано в первом аргументе.

Как уже было сказано, объявление массива, когда лишь его верхняя граница задаётся переменной, на самом деле, приводит к объявлению обычного массива, а не VLA. Значение имеет лишь нижняя граница массива. Это значит, что в следующем примере все объявления корректны и равноценны:

Тут, правда, возникает одна проблема. Какое из этих объявлений должно быть использовано для выдачи предупреждения, связанного с доступом к элементу, лежащему за пределами границ массива? В GCC 11 реализовано решение, опирающееся на первое объявление функции, и выдающее отдельное предупреждение, -Wvla-parameter, для любых последующих повторных объявлений, описывающих другое количество элементов в массиве. В результате при анализе кода, содержащего четыре вышеприведённых объявления функций, выдаются следующие предупреждения:

Параметры функций, представленные массивами

Современные C-программисты, опасаясь ситуаций с выделением в стеке памяти неограниченного объёма, как правило, используют массивы переменной длины не так часто, как их можно было бы использовать. Это справедливо даже для особых случаев, вроде объявлений функций, где применение таких массивов не только безопасно, но и помогает компилятору анализировать код. В некоторых проектах, где VLA не используются, применяется более простое решение, которое заключается в объявлении параметров функций в виде обычных массивов (например — T(N) ). При таком подходе ожидается, что вызывающая сторона предоставит функции доступ к некоему постоянному минимальному числу элементов (например — N ). Так, стандартная C-функция tmpnam() ожидает, что её аргумент будет указывать на массив, в котором содержится, как минимум, L_tmpnam элементов. Для того чтобы выразить это ожидание в явном виде, в GNU libc 2.34 эта функция объявлена так:

GCC 11 распознаёт такие конструкции. Когда компилятор выясняет, что при вызове функции ей передаётся массив, размер которого меньше, чем указано в объявлении функции, он выдаёт предупреждение. Например, учитывая то, что в Linux L_tmpnam равно 20, испытаем следующую функцию:

GCC выдаст следующее предупреждение:

GCC 11, в дополнение к вызовам функций, проверяет ещё и определения функций, в которых используются параметры-массивы, и выдаёт предупреждения при нахождении операций по работе с массивами, которые направлены на элементы, выходящие за постоянные границы массивов. Например, анализ объявления функции init_array() , код которой показан ниже, приводит к выдаче предупреждения -Warray-bounds .

Вот код функции:

А вот — предупреждение:

Аналогично, если речь идёт о повторных объявлениях функции с использованием VLA-параметров, GCC, кроме прочего, проверяет и их с учётом свойств параметра-массива первой функции, и выдаёт предупреждение -Warray-parameter в случае обнаружения несоответствий между первым объявлением и повторными объявлениями функции. Например, речь может идти о следующих объявлениях функции:

Вот предупреждения, которые выдаёт GCC:

Нюансы и ограничения

Те возможности GCC, о которых мы говорили, уникальны в одном интересном аспекте. А именно, их использование предусматривает применение к коду и простого лексического анализа, и более сложного динамического анализа, опирающегося на особенности выполнения этого кода. В теории, предупреждения, опирающиеся на лексический анализ, могут быть одновременно и надёжными, и полными (то есть — они не могут быть ни ложноположительными, ни ложноотрицательными). Предупреждения -Warray-parameter и -Wvla-parameters выдаются на основе лексического анализа кода, в результате они практически не страдают от подобных проблем. А вот предупреждения, основанные на динамическом анализе кода, с другой стороны, по своей природе, не являются ни надёжными, ни полными. Такие предупреждения, напротив, могут быть и ложноположительными, и ложноотрицательными.

▍Ложноотрицательные предупреждения

Для использования атрибутов access и выявления операций, которые направлены на области памяти, находящиеся за пределами допустимого адресного пространства, функция, к которой применяются такие атрибуты, не должна быть встроенной. Если же функция встроена в источник вызова, большинство её атрибутов обычно теряется. Это может помешать GCC в детектировании ошибок тогда, когда некорректные операции доступа к памяти не могут быть надёжно выявлены, основываясь лишь на коде тела встроенной функции. Например, функция genfname() из следующего примера использует, для генерирования имени временного файла в директории /tmp , функцию getpid() :

Так как в большинстве систем POSIX-функция getpid() возвращает 32-битное целое число, самое длинное имя, которое может сгенерировать эта функция, имеет длину 26 символов (10 символов с учётом INT_MAX , плюс — ещё 16 с учётом строки /tmp/tmpfile.txt ; тут надо учесть и ещё один байт для завершающего нуль-символа). Когда вызов genfname() в main() не является вызовом встроенной функции, GCC, как и ожидается, выдаёт следующее предупреждение:

Но если речь идёт о вызове встроенной функции, то никакого предупреждения не выводится. Здесь можно поэкспериментировать с обоими этими сценариями использования функций.

К слову сказать, если вам интересно, почему вызов sprintf() не приводит к выводу предупреждения -Wformat-truncation , то знайте, что дело в том, что компилятор не может что-либо узнать о результатах вызова getpid() .

▍Ложноположительные предупреждения

В целом можно сказать, что выявление некорректных операций доступа к памяти на основании аннотаций исходного кода, о которых мы тут говорили, подвержено тем же ограничениям и недостаткам, которые характерны для проверок кода, основанного на анализе особенностей его выполнения. Подробности об этом можно почитать здесь. В частности, определённый интерес в этом документе могут представлять несколько часто встречающихся проблем, относящихся к механизму аннотирования функций.

Как уже было сказано, в некоторых проектах используются параметры, представленные массивами, границы которых оформлены в виде констант. Это делается для того чтобы дать тому, кто вызывает функцию, подсказку о том, что он должен предоставить этой функции массив, в котором содержится, как минимум, столько элементов, сколько совпадает с количеством элементов в массиве-параметре. Но иногда разработчики функций не особенно строго придерживаются этого подхода. Например, функции используют массив лишь тогда, когда другой параметр имеет некое значение, а в других случаях не используют его. Так как нет ничего, что сообщило бы GCC об этой «особенности», предупреждение может быть выдано даже в том случае, если функция работает корректно. Мы рекомендуем не прибегать к вышеописанному подходу с массивами-параметрами в таких случаях.

Что дальше?

В GCC атрибут access можно использовать для выявления следующих проблем:

  • Доступ к областям памяти, находящимся за пределами допустимого адресного пространства (-Warray-bounds , -Wformat-overflow , -Wstringop-overflow и -Wstringop-overread ).
  • Доступ к перекрывающимся областям памяти (-Wrestrict).
  • Доступ к неинициализированным областям памяти (-Wuninitialized).

Мы, кроме того, рассматриваем возможность расширения, в какой-либо форме, атрибута access , для его применения к значениям, которые возвращают функции, а так же — к переменным. Аннотирование значений, возвращаемых функциями, позволит GCC детектировать попытки модификации иммутабельных объектов через указатели, возвращённые из функций (например — из функций getenv() или localeconv() ). И, аналогично, аннотирование глобальных переменных позволит выявлять попытки случайной модификации содержимого соответствующих объектов (например — это может быть массив указателей environ, хранящий сведения о переменных окружения).

Пользуетесь ли вы подсказками для компиляторов, которые помогают им проверять код?

Memory access out of bounds как исправить

I was getting this error when I try to load my Web game: enter image description here

My game is loaded at this domain: Highest Flavor Website Link

This is my project settings at the time of build export: enter image description here

Please give me some suggestion to solve this problem.

user avatar

3 Answers 3

I’ve finally fixed the same error in my project. Here is what i did:

Narrow down the line of code that crashes WebGL. Go through the painfull process of pinpointing the line of code that is the source of the error. I had this «luck» that error occured when I’ve hit button and tried to load UIScene in Addition mode. So at first I found the script in the scene which when disabled got rid of the crash. Then I’ve repeated the steps and digged deeper into lines. After 2 hours of builds, comments etc I’ve found that the code was not working was setting the new color for UnityEngine.UI.Image. Which was pretty weird because in playmode everything worked ok.

Find solution. I think my solution might not be universal but I think there is something going in the Unity gameloop/lifecycle when running WebGL. The source of the problem was that i set the field that was storing my UI.Image in the Start method and then in some function I’ve tried to change the color.
Property was not exposed in inspector.

then this line below was the cause of the crash

  1. Fixing — so I’ve added [SerializeField] attribute to the field and set it up in editor, dragged the Image into the inspector slot. An it fixed it. I suspect that WebGL build might perform some methods in different order, or maybe the multipleScene loaded together might to do with it. I’m not sure but the field was not set properly with previous code fot WebGL build.

Bonus: I’ve also fixed some issues that was not critical (not crushing WebGL game) but not working properly as expected in playmode. This also has got to do with trying to set some object in Start() method and then doing something on them. The collection was not set up (probably null) in WebGL.

And this also started working when I’ve added [SerializeField] to effects and hook it up in the scene.

RuntimeError: memory access out of bounds #175

hi, it’s me again. I’m getting the following error without any ways of debugging (for some reason, Mozilla does’t execute my code.

Here are the relevants bits of code.

Any idea how I could debug my own code ? What are the best practices when using the arena allocator etc ? I haven’t seen any example of using dynamic allocation on any of the projects using assemblyscript.

The text was updated successfully, but these errors were encountered:

Can you share full code of Sphere? It might be this.center is null or something else.

I uploaded the code here, I didn’t bother to setup the build.

btw you can use operator overloading for Vector3 class

I’m pretty an issue referenced that it wasn’t working. (issue #52 ?)

I finished prepare your example and don’t see any exceptions:
Update version: https://webassembly.studio/?f=gp6rm11tr5o

Printed result: 2

Which is AssemblyScript version you are use and how to install?

That’s storing a 32-bit value somewhere where the heap lives in an unsafe way, in turn corrupting the memory. Instead, either use —memoryBase to reserve some fixed-size space at the start of the memory, or use memory.allocate to dynamically allocate a suitable chunk of memory to store to.

Okay, so using the keyword new is definitely not the way to go along with memory.allocate because it doesn’t really care if I allocated memory before, right ?

So what’s the best way to use and instantiate objects without using new while still using memory allocation ? Creating a pointer and settings the values manually ? ..

The issue isn’t with the new keyword in particular, but that you have been mixing dynamic memory allocation and static loads/stores in an unsafe way (here: overwriting each other). If you for example know that you’ll need 8kb to store all your static stuff, use —memoryBase 8192 to reserve it before anything else. Compiler-generated static data and the dynamic heap will be placed after that then, so you can freely use those 8KB for manual things, i.e. for a pixel buffer. You can, of course, also allocate the pixel buffer dynamically within the program, for example var pixelBufferStart = memory.allocate(8192) and make your loads and stored relative to that, i.e. var pixelValue = load<u32>(pixelBufferStart + (y * WIDTH + x) * sizeof<u32>()) . Now, when accessing the pixel buffer from JS, obtain its start offset through export function getPixelBufferStart(): usize < return pixelBufferStart; >and use an Uint32Array view to work with it.

Closing. You are free to reopen it if there are more questions

Footer

© 2023 GitHub, Inc.

You can’t perform that action at this time.

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.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *