LINUX.ORG.RU

Вызов никогда не вызываемой функции

 , ,


3

5

Ваши ставки, господа: насколько безопасно на своём компьютере запускать такую программу? Не сотрет ли она вам корень?

Люблю C++.

#include <cstdlib>

typedef int (*Function)();

static Function Do;

static int EraseAll() {
  return system("yes");
}

void NeverCalled() {
  Do = EraseAll;  
}

int main() {
  return Do();
}
★★☆☆☆
Ответ на: комментарий от dzidzitop

std::mutex не является thread-safe и пользоваться им безопасно можно в очень ограниченном множестве случаев.

Весьма странная фича. Обычно мьютексы наоборот используют для того, чтоб сделать программу более потоко-безопасной.

А что там за проблемы с thread-safe у std::mutex?

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

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

если ты хочешь вместо гоночного автомобиля получить городской - брешь джаву. Если нужен блондинкокар, то берешь Golang (в котором вообще ничего нет, но и закиси азота тоже нет!) или JavaScript. А если ты вдруг родился с руками из жопы, то это сразу Clojure (которая медленней Джавы в 20 и более раз, и медленней C++ в незнамо сколько раз) - там накосячить вообще невозможно =)

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

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

Полностью с этим согласен. Но я говорил не о том, что конструкторы автомобиля должны сделать такой руль, который всегда блокируется, если его отпустить, а если опять за него взяться, — снова автоматически разблокируется. Я говорил о том, что они не должны намеренно его поворачивать налево (как и направо), если водитель его отпустит, с целью наказать такого незадачливого водителя. И см. мой камент о том же без ассоциаций с автомобилем.

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

а они и не поворачивают в данном случае. Есть один общий алгоритм, обрабатывающий инлайнинги. Не только этот случай, а еще тысячу разных. И они там внутри постоянно придумывают, как разруливать всевозможные UB. На некоторых случаях он работает хорошо, на некоторых - плохо (как сейчас). Причем, сейчас это не «плохо», а «возможно плохо», ибо у многих в этом треде не возникает проблем таким поведением. И ты что думаешь, ради одного сраного корнер кейса они пойдут править весь обобщенный алгоритм? Им делать больше нечего, что ли? Разработка проекта стоит бешеного бабла лучших специалистов, они его явно направят на реализацию чего-то более осмысленного.

олсо вангую, что чем более навороченными будут становиться оптимизации, тем меньше поведение в случае UB будет поддаваться какому-то рациональному объяснению с точки зрения «человеческой» логики

и другие сложные случаи тоже - например, многопоточность совершенно не понимабельна обычным человеком. И в джаве тоже! Можно просто в рамках карго-культа выполнять набор определенных «best practices» и молиться на иконы Энтони Вильямса, и может быть оно не сломается

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

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

Я тоже сначала так подумал, когда сказал, что это баг, который, по всей видимости, скоро будет исправлен. Но после того, как я увидел, что в другой ситуации они вместо 0 подставляют неопределённую инструкцию ud2, я начал в этом сильно сомневаться. Что за алгоритм может заставить компилятор генерить неисполняемые инструкции? Или это намёк на неопределённость кода? Но зачем так намекать, когда можно всё оставить как есть, чтоб программист сам нашёл ошибку в дебагере, а ещё лучше и ворнинг выдать, коли они чётко видят эту неопределённость.

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

у многих в этом треде не возникает проблем таким поведением.

Проблемы возникнут, когда им придётся дебажить чужой код из 100 модулей в 500 строк каждый, вылетающий из-за такой неинициализированной переменной, а вместо предупреждения и вызова 0 адреса они увидят в дебагере недопустимую ассемблерную команду ud2. Но это ещё пол беды: подумаешь, провозятся 2 дня вместо получаса, невелика потеря, типа сами виноваты, даже если не они этот код писали. Куда хуже будет, когда программа вообще не будет вылетать, но будет неправильно выполняться, потому что вместо предполагаемой, но не реализованной ветки выполнения будет выполняться случайная ветка. Такая программа легко может попасть в релиз и успешно эксплуатироваться многие годы, пока гром не грянет. А громом в зависимости от назначения программы могут оказаться и не сохранённые данные, и потерянные деньги, и даже человеческие жизни.

А на форуме конечно не возникает проблем, пока всё на стадии бла-бла-бла. Мы, типа, тру-программисты, а быдлокодеры пусть страдают.

aureliano15 ()
Ответ на: комментарий от dzidzitop

Нужно ещё безопасно обеспечить видимость инициализации mutex потоку перед использованием.

Вот это не совсем понял. Можно на пальцах в виде примитивного кода типа многопоточного хелловорлда?

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

про ud2 - лень читать комменты - но кто-то должен был оставить вот этот цикл из трех постов:

http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html
http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html
http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_21.html

в частности, обрати внимание на

Dereferencing a NULL Pointer: contrary to popular belief, dereferencing a null pointer in C is undefined. It is not defined to trap, and if you mmap a page at 0, it is not defined to access that page. This falls out of the rules that forbid dereferencing wild pointers and the use of NULL as a sentinel. NULL pointer dereferences being undefined enables a broad range of optimizations: in contrast, Java makes it invalid for the compiler to move a side-effecting operation across any object pointer dereference that cannot be proven by the optimizer to be non-null. This significantly punishes scheduling and other optimizations. In C-based languages, NULL being undefined enables a large number of simple scalar optimizations that are exposed as a result of macro expansion and inlining.

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

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

Another interesting case that bit someone recently happened when they had a (global) function pointer. A simplified example looks like this:

static void (*FP)() = 0;
static void impl() {
  printf("hello\n");
}
void set() {
  FP = impl;
}
void call() {
  FP();
}

which clang optimizes into:

void set() {}
void call() {
  printf("hello\n");
}

It is allowed to do this because calling a null pointer is undefined, which permits it to assume that set() must be called before call(). In this case, the developer forgot to call «set», did not crash with a null pointer dereference, and their code broke when someone else did a debug build.

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

это был хитрый план - я надеялся прижать оппонента тем, что он как истинный Ъ не ходит по ссылкам. Кажется, весь план вговно. Тем не менее, это официальное мнение разработчиков clang :)

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

Note: Construction and destruction of an object of a mutex type need not be thread-safe; other synchronization should be used to ensure that mutex objects are initialized and visible to other threads. — end note

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

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

It turns out that C is not a «high level assembler» like many experienced C programmers (particularly folks with a low-level focus) like to think, and that C++ and Objective-C have directly inherited plenty of issues from it.

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

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

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

Экий конфуз.

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

Note: Construction and destruction of an object of a mutex type need not be thread-safe; other synchronization should be used to ensure that mutex objects are initialized and visible to other threads. — end note

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

Понял. Спасибо за разъяснение.

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

А там, где не делает, надо допиливать. А пока не допилили, нечего переоптимизировать.

Тебе надо - возьми и допили. Стандарт не требует, а у разрабов другие приоритеты. Будь мужиком, сделай pull-request, это же опенсорц.

maverik ()
Ответ на: комментарий от aureliano15

Следует ли из этого, что производитель автомобиля может намеренно заложить в него функцию, которая при отпускании руля во время движения начнёт поворачивать его влево, на встречку? Ведь это соответствует стандарту (неопределённое направление).

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

В итоге, когда водитель (вопреки ПДД и рекомендациям автопроизводителя) отпусти руль и полетит на встречку, а потом убъется об камаз, я думаю, ни один суд не встанет на сторону водителя (или его родственников), если он подаст в суд на производителя автомобиля за то, что тот поехал на встречку, хотя водитель надеялся, что автомобиль поймет его маневр и, почему-то (судя по рассуждениям в этом треде), резко научившись автопилотировать, будет везти водятла, пока он не вернет руки обратно на руль.

maverik ()
Ответ на: комментарий от aureliano15

Зачем? gcc просто сегфолтит, и всё становится ясно. clang же меняет логику так, что нерабочая программа начинает работать, но так ли, как задумывалось? Имхо, это намного опаснее честного сегфолта.

Ты, видимо, местный юродивый. Тебе уже сказали: стандарт не требует от компилятора ничего при UB. В итоге каждый компилятор реализует это так, как хочет. gcc сегфолтится (хотя, UB на то и UB, что конкретное поведение зависит от конкретной программы и даже от конкретного запуска, я полагаю, и gcc может делать оптимизации, подобные clang, просто на этом примере это не работает), clang запускает какую-то другую функцию.

В итоге, никто из них ни лучше, ни хуже, просто реакция на данный конкретный пример разная.

maverik ()
Ответ на: комментарий от aureliano15

У меня вообще не собирается, что gcc, что clang:

11 : <source>:11:13: error: 'void NeverCalled()' defined but not used [-Werror=unused-function]
 static void NeverCalled() {
             ^~~~~~~~~~~
cc1plus: all warnings being treated as errors
Compiler exited with result code 1
maverik ()
Ответ на: комментарий от maverik

В итоге, никто из них ни лучше, ни хуже, просто реакция на данный конкретный пример разная.

gcc ничем не хуже трюки умеет выделывать. Чувак наверно в си недавно и не видел эпик треды в lkml, например (а там, емнип, даже уязвимость появлялась).

Вообще дело не в компиляторе, но до некоторых никогда не дойдет.

anonymous ()

Шланг всё-таки не полностью оптимизирует. Мог бы вывести constexpr для main и выполнить всё во время компиляции.

Короче, надо аффтарам шланга покинуть ещё идей как можно UB трактовать ;)

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

Шланг всё-таки не полностью оптимизирует. Мог бы вывести constexpr для main и выполнить всё во время компиляции.

Не мог бы, потому-что system не constexpr.

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

Шланг всё-таки не полностью оптимизирует. Мог бы вывести constexpr для main и выполнить всё во время компиляции.


Не мог бы, потому-что system не constexpr.

Так UB же всё позволяет!

Почему-бы сразу не взрывать комп вместе с программером?

Нет кода - нет проблем ;)

anonymous ()