Summary: | gcc8-c++ miscompiles functions returning non-void without return statements | ||||||
---|---|---|---|---|---|---|---|
Product: | Sisyphus | Reporter: | Aleksei Nikiforov <darktemplaralt> | ||||
Component: | gcc8-c++ | Assignee: | Gleb F-Malinovskiy <glebfm> | ||||
Status: | CLOSED FIXED | QA Contact: | qa-sisyphus | ||||
Severity: | normal | ||||||
Priority: | P3 | CC: | aen, glebfm, iv, lav, rider, zerg | ||||
Version: | unstable | ||||||
Hardware: | all | ||||||
OS: | Linux | ||||||
URL: | https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89218 | ||||||
Attachments: |
|
Description
Aleksei Nikiforov
2019-02-04 18:39:29 MSK
(In reply to comment #0) > Я считаю, что gcc8-c++ должен либо генерировать код, аналогичный более > старым версиям gcc, либо должен выдавать ошибки, а не предупреждения, вместо > генерации "сюрпризов". Было бы неплохо, но поскольку это UB, компилятор вправе делать всё что угодно. (В ответ на комментарий №1)
> (In reply to comment #0)
> > Я считаю, что gcc8-c++ должен либо генерировать код, аналогичный более
> > старым версиям gcc, либо должен выдавать ошибки, а не предупреждения, вместо
> > генерации "сюрпризов".
>
> Было бы неплохо, но поскольку это UB, компилятор вправе делать всё что угодно.
Я как знал, что такой комментарий будет :)
Да, это скорее всего UB, но поведение g++-8 по сравнению с g++-7 - явная деградация.
Created attachment 7988 [details]
gxx-bug-36038.cpp
Файл с примером, на котором g++ генерирует кривой код.
На машине x86_64 при использовании g++-7 -O2 генерируется нормальный код и получается следующий вывод:
Calling function
Running 1 time
Successfully returned from function
При использовании g++-8 -O0 внезапно тоже всё хорошо, аналогично прошлому случаю. Однако, при использовании g++-8 -O2 внезапно программа падает:
Calling function
Running 1 time
Ошибка сегментирования (стек памяти сброшен на диск)
Если посмотреть код функции somefunction, то при использовании g++-8 -O2 получается следующий код:
(gdb) $ disassemble somefunction
Dump of assembler code for function somefunction():
0x00000000004011e0 <+0>: sub $0x8,%rsp
0x00000000004011e4 <+4>: mov $0x1,%edx
0x00000000004011e9 <+9>: mov $0x402010,%esi
0x00000000004011ee <+14>: xor %eax,%eax
0x00000000004011f0 <+16>: mov $0x1,%edi
0x00000000004011f5 <+21>: callq 0x401030 <__printf_chk@plt>
Даже никакого ret нет, неудивительно что падает. В других вариантах (с g++-7 или с -O0) код функции намного длиннее и содержит retq.
Дим, а давай включим Werror для этого предупреждения и посмотрим что сломается при сборке ? (In reply to comment #2) > (В ответ на комментарий №1) > > (In reply to comment #0) > > > Я считаю, что gcc8-c++ должен либо генерировать код, аналогичный более > > > старым версиям gcc, либо должен выдавать ошибки, а не предупреждения, вместо > > > генерации "сюрпризов". > > > > Было бы неплохо, но поскольку это UB, компилятор вправе делать всё что угодно. > > Я как знал, что такой комментарий будет :) > Да, это скорее всего UB, но поведение g++-8 по сравнению с g++-7 - явная > деградация. Понятно же, почему так происходит: в gcc8 стало больше всяких оптимизаций, которые в случае UB делают фиг знает что. (In reply to comment #4) > Дим, а давай включим Werror для этого предупреждения и посмотрим что сломается > при сборке ? Было бы неплохо, но -Wreturn-type охватывает не только "no return statement in function returning non-void", но и другие. В нынешнем Сизифе "no return statement in function returning non-void" встречается в логе сборки 98 пакетов, а всего предупреждения от -Wreturn-type встречаются в логе сборки 277 пакетов. (В ответ на комментарий №5)
> Было бы неплохо, но -Wreturn-type охватывает не только "no return statement in
> function returning non-void", но и другие.
>
> В нынешнем Сизифе "no return statement in function returning non-void"
> встречается в логе сборки 98 пакетов, а всего предупреждения от -Wreturn-type
> встречаются в логе сборки 277 пакетов.
Я читаю это так: "в Сизифе до 98 пакетов потенциально сломано, но пересобрать надо будет 277 пакетов".
Возможно не каждое предупреждение говорит о подобной ошибке. Вопрос в том, сколько из этих пакетов реально уже незаметно сломано с gсс8-с++ (как минимум 1 уже починен, ещё 1 известен), сколько сломается при первой же пересборке, и когда на эти проблемы кто-нибудь нарвётся. И хорошо, если проблема будет вылезать сразу в виде крэша. Два случая, что я видел, приводят к крэшу. Но можно представить сценарии и похуже, UB ведь.
(In reply to comment #6) > (В ответ на комментарий №5) > > Было бы неплохо, но -Wreturn-type охватывает не только "no return statement in > > function returning non-void", но и другие. > > > > В нынешнем Сизифе "no return statement in function returning non-void" > > встречается в логе сборки 98 пакетов, а всего предупреждения от -Wreturn-type > > встречаются в логе сборки 277 пакетов. > > Я читаю это так: "в Сизифе до 98 пакетов потенциально сломано, но пересобрать > надо будет 277 пакетов". Можно сделать так, чтобы gcc и/или g++ генерили ошибку вместо предупреждения только для "no return statement in function returning non-void". Непонятно, сколько пакетов на самом деле потенциально сломано, и как пофиксить 98 пакетов. Вот пример попроще: static int foo(void) { } int main(void) { return foo(); } gcc на нём делает обычный код: main: .LFB1: .cfi_startproc xorl %eax, %eax ret .cfi_endproc g++ на нём делает код, который падает: main: .LFB1: .cfi_startproc .cfi_endproc Нашёл соответствующий апстримный баг: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87515 tl;dr: won't fix, используйте -Werror=return-type. Upstream statement: C++ says it is undefined even if you don't use the return value. This is different from C. GCC assumes that the path leading to the return without a value will not happen so it uses __builtin_unreachable call there. В качестве решения предлагаю сделать "no return statement in function returning non-void" неотключаемой ошибкой в g++. Давайте так и сделаем. Если починить g++ нельзя, то других вариантов не остаётся. Как минимум, следующие 45 пакетов нуждаются в починке: AlephOne-1.0.1-alt1.2 CGenius-1.9.9.6beta-alt1 OpenAD-20140315-alt4 antico-deluxe-0.1.96-alt1.2 arpage-0.3.3-alt3_24 bloboats-1.0.2-alt2 canorus-0.6svn-alt1.qa2 cooldown-24-alt1.1 duel3-0.1-alt3_0.24.20060225.qa1 extrema-4.4.5-alt2 fbg-0.9.1-alt3_12 fceux-2.2.3-alt1 freehdl-0.0.8-alt4 glob2-0.9.4.4-alt1.qa7.1 gnome-quod-0.2.3-alt2 imule-1.4.6-alt5 kbookocr-2.1.0-alt2 kismet-2014.02.R1-alt1 kumir-1.9.1.2810-alt2 lib2geom-20081103-alt1.5 libfreeimage-3.18.0-alt1 libplotmm-0.1.2-alt2 mapsoft-20180722-alt1 megafuse-1.0.0-alt3.3 mirall-1.0.2-alt5 nted-1.10.18-alt3 numptyphysics-0.3.160-alt1 openscada-0.9.0-alt2 packagekit-1.1.12-alt4 paris-traceroute-0.92-alt1 perl-JavaScript-V8-0.070-alt7.1 pfstools-2.1.0-alt3 printer-driver-splix-2.0.1-alt1.svn315 procbench-0.9.0a-alt3.1 qcat-0.5-alt5.1.qa1 qkismet-0.3.1-alt1.qa1 qt-fsarchiver-0.8.4.0-alt1 qtrainer-0.5.2-alt2.qa2 sprng-4.4-alt1 synaptic-0.58-alt20 verlihub-0.9.8e-alt2 verlihub-plugin-python-1.1-alt2.1.1.1 verlihub-plugins-0.1-alt2.qa2.1 verlihub-plugins-lua51-1.8.1-alt1.qa2 yaafe-0.64-alt2.git20130420 g++ -Wreturn-type генерит три вида ошибок: 1. no return statement in function returning non-void 2. control reaches end of non-void function 3. ISO C++ forbids declaration of 'main' with no type Первый вариант мы уже обсудили, в Сизифе таких пакетов на С++ минимум 45. Второй вариант не намного лучше первого, g++ точно так же генерит __builtin_unreachable() в конце функции, после чего в результате оптимизации от такой функции мало чего остаётся. В Сизифе таких пакетов на С++ минимум 60, из которых 25 уже проходят по первому варианту. Третий вариант выглядит безобидно, в Сизифе таких пакетов 5, из которых 2 уже проходят по первому варианту. Всего пакетов на С++, которые в результате включения -Werror=return-type в g++ перестанут собираться, как минимум 83. Серёг, посмотри, как мы сможем помочь с исправлениями в этих пакетах ? (В ответ на комментарий №16) > Серёг, посмотри, как мы сможем помочь с исправлениями в этих пакетах ? Да, всем сможем. Наверняка там в большинстве случаев в контекст не нужно особо вдаваться. Я решил просто добавить -Werror=return-type в g++ и не делать ошибку неотключаемой, поскольку в теории g++ может не увидеть, что из функции, в которой реализуется первый или второй вариант -Wreturn-type, на самом деле есть выход через какую-нибудь неразмеченную noreturn- или throw-функцию. отличное сбалансированное решение. Может быть, перед или после выкладывания стоит рассказать об этой проблеме в рассылке (как и о стандартных способах починки) ? gcc8-8.2.1-alt4 -> sisyphus: Thu Feb 07 2019 Dmitry V. Levin <ldv@altlinux> 8.2.1-alt4 - Added ppc64le support (by glebfm@). - Fixed profiledbootstrap build (by glebfm@). - g++: enabled -Werror=return-type by default (closes: #36038). - libcc1.so.0: cleaned up using a fixed libtool (closes: #36045). |