LINUX.ORG.RU

Нелогичная логика

 


0

2

Куприв!

Пишу я тут очередной ляс и в процессе компиляции компилятор и даже линковщик(!) выдаёт мне такие предупреждения:

cc -Wall -O2   -c -o sim7600-at.o sim7600-at.c
sim7600-at.c: In function 'simApplicationToolkit':
sim7600-at.c:1013:17: warning: implicit declaration of function 'gets'; did you mean 'fgets'? [-Wimplicit-function-declaration]
 1013 |                 gets( userAns );
      |                 ^~~~
      |                 fgets
cc -s  sim7600-at.o utils_pdu_sms.o   -o sim7600-at
/usr/bin/ld: sim7600-at.o: in function `simApplicationToolkit':
sim7600-at.c:(.text+0xf91): warning: the `gets' function is dangerous and should not be used.

Справедливо, но тогда почему нет подобного предупреждения при использовани strncpy, например, которая тоже дырявая, даже не смотря на n?

★★
Ответ на: комментарий от u5er

Просто implicit function declaration указывает на использование функции без декларации.

Здесь указано, что функция удалена из C11: https://en.cppreference.com/w/c/io/gets

Хотя предупреждение линковщика, кажется, к этому не имеет отношения.

kaldeon
()
Последнее исправление: kaldeon (всего исправлений: 2)
Ответ на: комментарий от u5er

implicit declaration of function ‘gets’; did you mean ‘fgets’?

переведи уже - дословно «неявная декларация функции gets». ты либо живи с этим, либо поставь компилятору опцию о запрете неявных деклараций.

карочи у тебя два баги разом

  1. функция обьявлена неявно (вызвана, но компилятор не видит ее заголовка) - это компилятор сказал.

  2. функция deprecated - это шибко умный линкер сказал.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 2)

Я не знаю, что вы подразумеваете под дырявостью, но они подразумевают исключительно то, что без знания размера буфера всё может быть очень плохо. В этом контексте они предлагают использовать fgets вместо gets и strncpy вместо strcpy.

В man’ах кстати всё про это сказано.

unDEFER ★★★★★
()

Хороший пример как не надо задавать вопросы.

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

Самому стало интересно почему именно линковщик выдает здесь ошибку, и как именно задается атрибут небезопасности функции.

a1ba ★★
()
Последнее исправление: a1ba (всего исправлений: 1)
Ответ на: комментарий от a1ba

как именно задается атрибут небезопасности функции.

https://ninjalj.blogspot.com/2011/11/your-own-linker-warnings-using-gnu.html

Если коротко, в объектном файле создаётся секция .gnu.warning.gets с текстом предупреждения

red75prim ★★★
()
Последнее исправление: red75prim (всего исправлений: 1)

даже линковщик(!)

Это чтоб ты был в курсе что какая-то сторонняя библиотека, которой ты захотел воспользоваться, с багом.

почему нет подобного предупреждения при использовани strncpy, например, которая тоже дырявая, даже не смотря на n?

Что значит дырявая? Дырявым может быть твой код, если он плохо написан, а в самой strncpy ничего плохого нет (надо просто маны читать и понимать, что именно она делает и зачем). И даже для strcpy, хоть оно и проблемное, вполне есть нормальные варианты использования, например: { char s[10]; strcpy(s, "qwe"); }.

А вот полностью безопасных способов использования gets практически не существует. Есть один только: ты заранее редиректишь stdin на какой-то файл, содержимое которого уже 100% проверено на отсутствие переполнений, и читаешь его с помощью gets. Но это какая-то странная штука, так очевидно никто делать не станет. Так что gets это гарантированно либо баг в 99%+ случаев), либо сомнительный код который всё равно надо исправить (в остальных меньше 1%).

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)
Ответ на: комментарий от a1ba

Нет, ТС спрашивает не про конкретное предупреждение, а про то почему про strncpy его нет. Но, и правда, почему-то все отвечают какую-то чушь не по теме - про то почему есть предупреждение про gets (автор это и без вас знает) или про то как его убрать (и это он не спрашивал).

firkax ★★★★★
()
Ответ на: комментарий от firkax

Но, и правда, почему-то все отвечают какую-то чушь не по теме - про то почему есть предупреждение про gets (автор это и без вас знает)

Пишу я тут очередной ляс и в процессе компиляции компилятор и даже линковщик(!) выдаёт мне такие предупреждения:
Импортнул. Он в stdio.h. Суть в предупреждении, что функция небезопасна.

судя по авторским блужданиям, сути первого варнинга он не понимает. Иначе не писал бы, что «импортнул».

alysnix ★★★
()
Ответ на: комментарий от alysnix

Нет там никаких блужданий, оба варнинга по причине депрекации этой функции, всё правильно. Технические подробности (про удаление её из .h) уже вторичны. Да и вопроса об этом не было. Вопрос был про strncpy.

firkax ★★★★★
()
Ответ на: комментарий от anonymous

-std лучше указывать (в скрипте) при любой компиляции, чтобы она была детерминистична а не зависела от непредвиденных обстоятельств в будущем.

firkax ★★★★★
()
Ответ на: комментарий от firkax

оба варнинга по причине депрекации этой функции, всё правильно.

нет. «вызов без декларации» не имеет отношения к депрекации. для депрекации другие варнинги. один из которых выплюнул линкер.

и никаких варнингов о неявной декларации быть не должно. это или описка или неверное представление о том, как надо оформлять код(даже если это не описка).

alysnix ★★★
()

всё логично - strncpy хоть и не затерменирует строку, сам по себе дальше n не убежит.
А вот в случае с gets стек затрётся прям самой функцией, т.к длина буфера там ничем не ограничена. Для сравнения, даже strcpy может вполн безопасно использоваться, когда исхолная строка - null-terminated литерал - буффер гарантировано ограничен им и ошибкой это будет только если длина буфера меньше длины исходного буффера/литерала, что можно проконтроллировать извне в отличие от буфера в gets

mittorn ★★★★★
()

Для отписавшихся по поводу implicit declaration привожу минимальный пример:

#include <stdio.h>


int main( int argc, char* argv[], char* envp[] ){
	
	char buf[128];
	
	
	printf( ">" );
	fflush( stdout );
	gets( buf );
	printf( "buf: %s\n", buf );
	printf( "End of program.\n" );
	
	return 0;
}
$ make
cc -Wall -O2   -c -o main.o main.c
main.c: In function 'main':
main.c:11:9: warning: implicit declaration of function 'gets'; did you mean 'fgets'? [-Wimplicit-function-declaration]
   11 |         gets( buf );
      |         ^~~~
      |         fgets
cc -s  main.o   -o main
/usr/bin/ld: main.o: in function `main':
main.c:(.text.startup+0x3a): warning: the `gets' function is dangerous and should not be used.

Но это всё оффтоп. Я поинтересовался почему в процессе компиляции выводится предупреждение о том, что gets опасный, но про другие небезопасные функции (например strncpy) предупреждений нет, а не почему выдаётся предупреждение implicit declaration.

u5er ★★
() автор топика
Последнее исправление: u5er (всего исправлений: 1)
Ответ на: комментарий от anonymous
$ echo __STDC_VERSION__ | gcc -std=gnu99 -E -
# 0 "<stdin>"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 0 "<command-line>" 2
# 1 "<stdin>"
199901L
$ echo __STDC_VERSION__ | gcc -E -
# 0 "<stdin>"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 0 "<command-line>" 2
# 1 "<stdin>"
201710L

u5er ★★
() автор топика

Используя gets принципиально невозможно написать корректный код. Поэтому её пометили, как опасную. В strncpy никаких проблем нет, как и в strcpy. Никаких оснований как либо помечать их - нет.

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 1)
Ответ на: комментарий от u5er

Кем предлагается? Мной - точно не предлагается. Использовать нужно то, что уместно в каждом конкретном случае.

Например в этом коде использовать что-либо кроме strcpy не уместно:

char buf[5];
strcpy(buf, "1234");
vbr ★★★★★
()
Ответ на: комментарий от u5er

И да, хотя gets удалили из stdio.h для новых стандартов Си, но он (символ) остался в динамически загружаемой glibc, чтобы можно было линковать бинари скомпилированные старыми стандартами. Поэтому и собирается твоя программа с удаленной gets.

anonymous
()
Ответ на: комментарий от u5er

Я не могу отвечать за измышления редакторов википедии. Идея того, что можно в C заменить несколько функций и сделать его безопасным языком смешна. C это небезопасный язык по определению. И в такой постановке ничего страшного в использовании strcpy нет, если твой код написан так, что переполнения буфера не будет. Такой код написать несложно, я выше привёл пример такого кода.

В случае с gets невозможно написать код так, чтобы переполнения не было. По крайней мере пока stdin привязан к внешнему миру и в него потенциально могут поступать любые данные. Разница принципиальная.

vbr ★★★★★
()
Ответ на: комментарий от u5er

Ладно, ладно. Подключил. Теперь можешь спать спокойно :)

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

alysnix ★★★
()
Ответ на: комментарий от u5er

strncpy и strlcpy - функции для совершенно разных нужд. Использовать как strlcpy вместо strncpy, так и strncpy вместо strlcpy нельзя - они друг друга не заменяют. Но да, есть категория некомпетентных программистов, которые бездумно суют как первую, так и вторую, создавая в итоге дефективный софт.

firkax ★★★★★
()
Ответ на: комментарий от vbr

И ещё добавлю, что любые проверки это лишние циклы процессора. C это тот язык, который принципиально проектируется так, чтобы компилироваться в быстрый код. В нём нет проверок границ массивов, потому, что это позволяет генерировать более быстрый код. Идти против течения, проектируя псевдо-безопасный API для принципиально опасного языка - я считаю странной идеей. Нужен безопасный язык - берите Rust. Или Go. Или Python.

vbr ★★★★★
()
Ответ на: комментарий от u5er

я за жизнью этой gets не слежу, ибо работаю на с++.

у меня вот только вопрос есть. если ее вытащили из хидеров, каким образом ее видит линкер. или ее там оставили для своих нужд и обратной совместимости?

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

alysnix ★★★
()
Ответ на: комментарий от u5er

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

strlcpy да, для копирования ноль-терминированных строк в новый буфер с защитой от битья памяти.

Но во многих случаев для этого не годится ни strncpy, ни strlcpy, а нужно писать код типа такого:

len = strlen(src);
if(len>=buflen) { fprintf(stderr,"string overflow!\n"); abort(); }
bcopy(src, buf, len);
buf[len] = 0;
Потому как молчаливое обрезание конца строки само по себе может нарушить логику работы программы и привести к уязвимостям, даже если копирование само по себе и не устроит переполнение буфера. Например, если из-за такого обрезания испортится имя какого-то файла - ты в итоге можешь читать не те данные или даже записывать данные куда-то не туда, возможно затирая другие которые нельзя было трогать.

firkax ★★★★★
()
Ответ на: комментарий от vbr

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

Нужен безопасный язык - берите Rust. Или Go. Или Python.

Спасибо, но я возьму аду при необходимости.

u5er ★★
() автор топика
Ответ на: комментарий от u5er

Это позволит читать данные за пределами буфера.

Читать байты за пределами буфера ты можешь всегда, и никакие strlcpy тебе в этом не помешают. Всё зависит от того, как ты пишешь код.

firkax ★★★★★
()
Ответ на: комментарий от firkax

len = strlen(src);

ты можешь считать длину во время копирования. и если не прошла полная копия в буфер заданной длины - абортироваться в этом случае.

карочи брать длину заранее тут избыточно.

alysnix ★★★
()
Ответ на: комментарий от firkax

Я имел в виду, что в конце обычных си-строк ожидается нуль-терминатор. Если его там нет, то строка будет читаться до тех пор, пока что-нибудь не случится. А что именно - это уже зависит от.

u5er ★★
() автор топика
Последнее исправление: u5er (всего исправлений: 1)
Ответ на: комментарий от u5er

Смысл strncpy в том, чтобы забить принимающий буфер нулями. Это не strcpy с дополнительной проверкой. Он забивает весь принимающий буфер нулями после скопированных данных. Если тебе это поведение нужно - ты его используешь. Если не нужно - не используешь. В 99% случаев такое поведение не нужно.

strcpy с проверкой в стандартной библиотеке нет. strlcpy это из BSD и в стандартном C такой функции нет. Если нужно такое поведение - значит либо пишешь такую функцию сам, либо подключаешь libbsd.

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 1)
Ответ на: комментарий от u5er

ты фиркакса не слушай. уже одно то, что он всем блевики ставит, говорит о том, что он слишком экспрессивен.

ему важно показать наружу свою реакцию. он видимо человек южных кровей и широких кепок.

alysnix ★★★
()