Skip to content
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

Критерий -- использование malloc/calloc без sizeof #20

Closed
mirrin00 opened this issue Jan 19, 2024 · 9 comments · Fixed by #28
Closed

Критерий -- использование malloc/calloc без sizeof #20

mirrin00 opened this issue Jan 19, 2024 · 9 comments · Fixed by #28

Comments

@mirrin00
Copy link

Зачастую память выделяется под какой-то тип (int, структура и тд). Необходимо использовать sizeof, чтобы узнать размер, иначе это ошибка, которую нужно подсветить.

Плохой пример:

int *arr = (int*)malloc(40);
double *arrf = (double*)calloc(10, 8);

Хороший пример:

int *arr = (int*)malloc(10 * sizeof(int));
double *arrf = (double*)calloc(10, sizeof(double);
@mirrin00
Copy link
Author

Нужно проверить, что этого нет в oclint

@jcdkiki
Copy link
Collaborator

jcdkiki commented Feb 9, 2024

Добавлено правило (критерий??) MallocSizeofRule. Оно ищет функции malloc и realloc. Если аргументы в этих функциях не имеют вид sizeof(...) * SOMETHING, тогда правило сообщает об ошибке.

Очень часто у одногруппников и не только видел такой код:

str = realloc(str, capacity + BLOCKSIZE * sizeof(char*));

Забывали скобки поставить. Не думаю, что код в таком виде доходил до Вас (если вдруг доходил, то удивительно, как он проходил проверку на e.moevm)
В общем, в текущей реализации правило отлавливает и такой код.

Сейчас это правило не смотрит, какой тип или выражение лежит внутри sizeof. Но если это окажется критически важным, то можно будет что-нибудь с этим придумать

@jcdkiki
Copy link
Collaborator

jcdkiki commented Feb 9, 2024

Еще, кстати, можно придумать что-нибудь, про что часто говорил Ярослав Сергеевич на лекциях. А именно явно приводить указатель, возвращаемый malloc/realloc к нужному типу.
Т.е. вместо str = realloc(str, capacity * sizeof(char)) писать str = (char*)realloc(str, capacity * sizeof(char)).

Не совсем уверен, как это можно сделать, но очень похоже, что достаточно посмотреть на то, где лежит функция. Если лежит в clang::ImplicitCastExpr, то преобразуется неявно, и это плохо.

@jcdkiki jcdkiki closed this as completed Feb 9, 2024
@jcdkiki jcdkiki reopened this Feb 10, 2024
@jcdkiki
Copy link
Collaborator

jcdkiki commented Feb 29, 2024

@Mirrin По поводу функций внутри функций. Эта фича является расширением gcc, поэтому clang, силами которого пользуется OCLint, сигнализирует об ошибке и OCLint отказывается анализировать файл:

Compiler Errors:
(please be aware that these errors will prevent OCLint from analyzing this source code)

/home/jcdkiki/code/oclint_extensions/examples/ex-nested-func/main.c:5:2: illegal storage class on function
/home/jcdkiki/code/oclint_extensions/examples/ex-nested-func/main.c:10:2: function definition is not allowed here

main.c:

#include <stdio.h>

int main()
{
	void nested_func();
	nested_func();
	printf("MAIN FUNCTION\n");

	void nested_func()
	{
		printf("NESTED FUNCTION\n");
		return;
	}

	printf("MAIN FUNCTION\n");
	nested_func();

	return 0;
}

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

У себя в локальной копии репозитории сделал ветку LambdaFunctionRule и написал там критерий, который отлавливает лямбда-выражения. Если он вдруг нужен, могу запушить его сюда.

@jcdkiki jcdkiki closed this as completed Feb 29, 2024
@jcdkiki jcdkiki reopened this Feb 29, 2024
@jcdkiki
Copy link
Collaborator

jcdkiki commented Feb 29, 2024

Кстати, получается неприятная ситуация. Если студент воспользуется функцией внутри функции, то такой код пройдет проверку на emoevm, но OCLint проверять его откажется, то есть не отловит всякие ошибки в работе, допущенной к защите, которые хотелось бы отловить. Бог знает какие еще есть различия у gcc и clang и как это проблему решать. Но о её существовании, наверное, стоит знать заранее.

@mirrin00
Copy link
Author

Забывали скобки поставить

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

@Mirrin По поводу функций внутри функций. Эта фича является расширением gcc, поэтому clang, силами которого пользуется OCLint, сигнализирует об ошибке и OCLint отказывается анализировать файл:

А какой код возврата будет? По приведенным логам я вижу, что есть отдельная секция compiler errors. Вероятно, анализ такого вывода можно сделать в проверяющей системе и сказать, что программа не компилируется

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

Лямбды могут иметь доступ к локальным функциями

@jcdkiki
Copy link
Collaborator

jcdkiki commented Mar 25, 2024

Не понял, где нужны скобки

Не понял, причем тут скобки внутри макросов. Очень часто, когда меня просили с кодом помочь, натыкался на подобные ошибки:

#define CAPACITY_ADD 10
...
str = (char*)realloc(str, capacity + CAPACITY_ADD * sizeof(char));

Такая программа скомпилируется без ошибок и варнингов. И, если sizeof(char) == 1 (что есть правда в 99.9% случаев), то программа отработает без ошибок, как и было задумано автором, а автор даже и не поймет, что ошибся. Но, согласитесь, в последней строчке не хватает скобок. Надо написать так:

str = (char*)realloc(str, (capacity + CAPACITY_ADD) * sizeof(char));

Если рассмотреть синтаксическое дерево "неправильного" и "правильного" кода то схематично они будут выглядеть вот так:

  • Неправильный код:
вызов функции:
  - имя функции: realloc
  - аргумент №1: str
  - аргумент №2: сумма
      - первый член суммы: capacity
      - второй член суммы: CAPACITY_ADD * sizeof(char)
  • Правильный код:
вызов функции:
  - имя функции: realloc
  - аргумент №1: str
  - аргумент №2: произведение
      - первый член произведения: (capacity + CAPACITY_ADD)
      - второй член произведения: sizeof(char)

Разница налицо. Этот критерий как раз таки и смотрит на аргумент №2 в вызове функции realloc. И смотрит, чтобы там лежало ПРОИЗВЕДЕНИЕ вида number_of_elements * sizeof(element) или наоборот: sizeof(element) * number_of_elements. Иначе получается какая-то каша.

@jcdkiki
Copy link
Collaborator

jcdkiki commented Mar 25, 2024

А какой код возврата будет?

Из моих наблюдений, поведение такое:
Если есть Compiler Errors, тогда код ненулевой.
Остальные сообщения (Compiler Warnings - варнинги компилятора и OCLint Summary - собственно сами сообщения критериев, ради которых это всё и затевается) ошибками не считаются, поэтому на код возврата никак не влияют

@mirrin00
Copy link
Author

Не понял, причем тут скобки внутри макросов. Очень часто, когда меня просили с кодом помочь, натыкался на подобные ошибки:

Зависит от того, как считается capacity, его же можно не просто увеличивать на CAPACITY_ADD, а сразу в байтах хранить: capacity += CAPACITY_ADD * sizeof(my_char)
Спорный критерий в проверке на такое.

  • Что будет, если capacity увеличить заранее и его передавать в realloc?
  • А если в realloc по хитрому передаётся выражение: realloc(old_ptr, get_new_size(ptr_my_struct))?

Из моих наблюдений, поведение такое:
Если есть Compiler Errors, тогда код ненулевой.
Остальные сообщения (Compiler Warnings - варнинги компилятора и OCLint Summary - собственно сами сообщения критериев, ради которых это всё и затевается) ошибками не считаются, поэтому на код возврата никак не влияют

Отлично, тогда ваши скрипты для запуска oclint должны сохранять этот код возврата, чтобы проверяющая система могла проверить его и отловить ошибки компиляции

@jcdkiki jcdkiki linked a pull request Jul 25, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants