gcc-10.0.1 विशिष्ट सेगफ़ॉल्ट


23

मेरे पास सी संकलित कोड के साथ एक आर पैकेज है जो काफी समय से अपेक्षाकृत स्थिर है और अक्सर प्लेटफ़ॉर्म और कंपाइलरों (विंडोज़ / ओएक्सएक्स / डेबियन / फेडोरा जीसीसी / क्लैंग) की एक विस्तृत विविधता के खिलाफ परीक्षण किया जाता है।

हाल ही में पैकेज का परीक्षण करने के लिए एक नया प्लेटफ़ॉर्म जोड़ा गया:

Logs from checks with gcc trunk aka 10.0.1 compiled from source
on Fedora 30. (For some archived packages, 10.0.0.)

x86_64 Fedora 30 Linux

FFLAGS="-g -O2 -mtune=native -Wall -fallow-argument-mismatch"
CFLAGS="-g -O2 -Wall -pedantic -mtune=native -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong -fstack-clash-protection -fcf-protection"
CXXFLAGS="-g -O2 -Wall -pedantic -mtune=native -Wno-ignored-attributes -Wno-deprecated-declarations -Wno-parentheses -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong -fstack-clash-protection -fcf-protection"

जिस बिंदु पर संकलित कोड तुरंत इन पंक्तियों के साथ segfaulting शुरू कर दिया:

 *** caught segfault ***
address 0x1d00000001, cause 'memory not mapped'

मैं अनुकूलन स्तर के साथ rocker/r-basedocker कंटेनर का उपयोग करके लगातार segfault को पुन: पेश करने में सक्षम रहा हूं । कम अनुकूलन चलाने से समस्या से छुटकारा मिलता है। Valgrind (-O0 और -O2), UBSAN (gcc / clang) सहित किसी भी अन्य सेट-अप को चलाने से कोई समस्या नहीं होती है। मुझे भी यकीन है कि यह भाग गया था , लेकिन डेटा नहीं है।gcc-10.0.1-O2gcc-10.0.0

मैंने इसके gcc-10.0.1 -O2साथ संस्करण चलाया gdbऔर कुछ ऐसा देखा जो मुझे अजीब लगता है:

जीडीबी बनाम कोड

हाइलाइट किए गए सेक्शन के माध्यम से कदम बढ़ाते समय, यह प्रतीत होता है कि सरणियों के दूसरे तत्वों का आरंभीकरण छोड़ दिया गया है ( आर के नियंत्रण में लौटने पर उस आत्म कचरा के R_allocचारों ओर एक आवरण mallocइकट्ठा होता है; आर पर लौटने से पहले सेगफॉल्ट होता है)। बाद में, संयुक्त राष्ट्र के प्रारंभिक तत्व (gcc.10.0.1 -O2 संस्करण में) तक पहुँचने पर प्रोग्राम क्रैश हो जाता है।

मैंने इसे कोड में हर जगह स्पष्ट रूप से तत्व को प्रारंभिक रूप से निर्दिष्ट करके निर्धारित किया था जो अंततः तत्व का उपयोग करने के लिए प्रेरित करता था, लेकिन यह वास्तव में एक खाली स्ट्रिंग के लिए आरंभीकृत किया जाना चाहिए था, या कम से कम मुझे यह मान लेना चाहिए था।

क्या मुझे कुछ स्पष्ट याद आ रहा है या कुछ बेवकूफ कर रहा है? दोनों यथोचित रूप से संभव हैं क्योंकि C मेरी अब तक की दूसरी भाषा है । यह अजीब है कि यह अभी अभी फसली है, और मैं यह पता नहीं लगा सकता कि संकलक क्या करने की कोशिश कर रहा है।


अद्यतन : इसे फिर से शुरू करने के निर्देश, हालांकि यह केवल तब तक पुन: पेश करेगा जब तक debian:testingडॉक कंटेनर में gcc-10है gcc-10.0.1। इसके अलावा, अगर आप मुझ पर भरोसा नहीं करते हैं तो सिर्फ ये कमांड न चलाएं

क्षमा करें, यह एक न्यूनतम प्रजनन योग्य उदाहरण नहीं है।

docker pull rocker/r-base
docker run --rm -ti --security-opt seccomp=unconfined \
  rocker/r-base /bin/bash
apt-get update
apt-get install gcc-10 gdb
gcc-10 --version  # confirm 10.0.1
# gcc-10 (Debian 10-20200222-1) 10.0.1 20200222 (experimental) 
# [master revision 01af7e0a0c2:487fe13f218:e99b18cf7101f205bfdd9f0f29ed51caaec52779]

mkdir ~/.R
touch ~/.R/Makevars
echo "CC = gcc-10
CFLAGS = -g -O2 -Wall -pedantic -mtune=native -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong -fstack-clash-protection -fcf-protection
" >> ~/.R/Makevars

R -d gdb --vanilla

फिर आर कंसोल में प्रोग्राम चलाने के लिए टाइप runकरने के बाद gdb:

f.dl <- tempfile()
f.uz <- tempfile()

github.url <- 'https://github.com/brodieG/vetr/archive/v0.2.8.zip'

download.file(github.url, f.dl)
unzip(f.dl, exdir=f.uz)
install.packages(
  file.path(f.uz, 'vetr-0.2.8'), repos=NULL,
  INSTALL_opts="--install-tests", type='source'
)
# minimal set of commands to segfault
library(vetr)
alike(pairlist(a=1, b="character"), pairlist(a=1, b=letters))
alike(pairlist(1, "character"), pairlist(1, letters))
alike(NULL, 1:3)                  # not a wild card at top level
alike(list(NULL), list(1:3))      # but yes when nested
alike(list(NULL, NULL), list(list(list(1, 2, 3)), 1:25))
alike(list(NULL), list(1, 2))
alike(list(), list(1, 2))
alike(matrix(integer(), ncol=7), matrix(1:21, nrow=3))
alike(matrix(character(), nrow=3), matrix(1:21, nrow=3))
alike(
  matrix(integer(), ncol=3, dimnames=list(NULL, c("R", "G", "B"))),
  matrix(1:21, ncol=3, dimnames=list(NULL, c("R", "G", "B")))
)

# Adding tests from docs

mx.tpl <- matrix(
  integer(), ncol=3, dimnames=list(row.id=NULL, c("R", "G", "B"))
)
mx.cur <- matrix(
  sample(0:255, 12), ncol=3, dimnames=list(row.id=1:4, rgb=c("R", "G", "B"))
)
mx.cur2 <-
  matrix(sample(0:255, 12), ncol=3, dimnames=list(1:4, c("R", "G", "B")))

alike(mx.tpl, mx.cur2)

Gdb में निरीक्षण करना बहुत जल्दी दिखाता है (यदि मैं सही तरीके से समझता हूं) जो CSR_strmlen_xउस स्ट्रिंग को एक्सेस करने की कोशिश कर रहा है जिसे आरंभीकृत नहीं किया गया था।

अद्यतन 2 : यह एक अत्यधिक पुनरावर्ती कार्य है, और इसके शीर्ष पर स्ट्रिंग आरंभीकरण बिट को कई बार, कई बार कहा जाता है। यह ज्यादातर बी / सी है मैं आलसी हो रहा था, हमें केवल एक बार के लिए आरंभीकृत तार की आवश्यकता होती है, हम वास्तव में कुछ ऐसी चीज का सामना करते हैं जिसे हम पुनरावृत्ति में रिपोर्ट करना चाहते हैं, लेकिन हर बार जब किसी चीज का सामना करना संभव होता है, तो इसे शुरू करना आसान था। मैं इसका उल्लेख करता हूं क्योंकि आप आगे जो देखेंगे, वह कई इनीशियलाइज़ेशन दिखाता है, लेकिन उनमें से केवल एक (संभवतः पता <0x1400000001> के साथ एक) का उपयोग किया जा रहा है।

मैं इस बात की गारंटी नहीं दे सकता कि जो सामान मैं यहाँ दिखा रहा हूँ, वह सीधे उस तत्व से संबंधित है, जो सीगफॉल्ट का कारण बना (हालाँकि यह एक ही अवैध पता है), लेकिन जैसा कि @ nate-eldredge ने पूछा है कि यह दिखाता है कि ऐरे तत्व नहीं है प्रारंभिक या तो रिटर्न से पहले या कॉलिंग फ़ंक्शन में वापस आने के बाद। ध्यान दें कि कॉलिंग फ़ंक्शन इनमें से 8 को इनिशियलाइज़ कर रहा है, और मैं उन सभी को दिखाता हूं, जिनमें सभी कचरा या दुर्गम मेमोरी से भरे हुए हैं।

यहाँ छवि विवरण दर्ज करें

अद्यतन 3 , विचाराधीन कार्य में गड़बड़ी:

Breakpoint 1, ALIKEC_res_strings_init () at alike.c:75
75    return res;
(gdb) p res.current[0]
$1 = 0x7ffff46a0aa5 "%s%s%s%s"
(gdb) p res.current[1]
$2 = 0x1400000001 <error: Cannot access memory at address 0x1400000001>
(gdb) disas /m ALIKEC_res_strings_init
Dump of assembler code for function ALIKEC_res_strings_init:
53  struct ALIKEC_res_strings ALIKEC_res_strings_init() {
   0x00007ffff4687fc0 <+0>: endbr64 

54    struct ALIKEC_res_strings res;

55  
56    res.target = (const char **) R_alloc(5, sizeof(const char *));
   0x00007ffff4687fc4 <+4>: push   %r12
   0x00007ffff4687fc6 <+6>: mov    $0x8,%esi
   0x00007ffff4687fcb <+11>:    mov    %rdi,%r12
   0x00007ffff4687fce <+14>:    push   %rbx
   0x00007ffff4687fcf <+15>:    mov    $0x5,%edi
   0x00007ffff4687fd4 <+20>:    sub    $0x8,%rsp
   0x00007ffff4687fd8 <+24>:    callq  0x7ffff4687180 <R_alloc@plt>
   0x00007ffff4687fdd <+29>:    mov    $0x8,%esi
   0x00007ffff4687fe2 <+34>:    mov    $0x5,%edi
   0x00007ffff4687fe7 <+39>:    mov    %rax,%rbx

57    res.current = (const char **) R_alloc(5, sizeof(const char *));
   0x00007ffff4687fea <+42>:    callq  0x7ffff4687180 <R_alloc@plt>

58  
59    res.target[0] = "%s%s%s%s";
   0x00007ffff4687fef <+47>:    lea    0x1764a(%rip),%rdx        # 0x7ffff469f640
   0x00007ffff4687ff6 <+54>:    lea    0x18aa8(%rip),%rcx        # 0x7ffff46a0aa5
   0x00007ffff4687ffd <+61>:    mov    %rcx,(%rbx)

60    res.target[1] = "";

61    res.target[2] = "";
   0x00007ffff4688000 <+64>:    mov    %rdx,0x10(%rbx)

62    res.target[3] = "";
   0x00007ffff4688004 <+68>:    mov    %rdx,0x18(%rbx)

63    res.target[4] = "";
   0x00007ffff4688008 <+72>:    mov    %rdx,0x20(%rbx)

64  
65    res.tar_pre = "be";

66  
67    res.current[0] = "%s%s%s%s";
   0x00007ffff468800c <+76>:    mov    %rax,0x8(%r12)
   0x00007ffff4688011 <+81>:    mov    %rcx,(%rax)

68    res.current[1] = "";

69    res.current[2] = "";
   0x00007ffff4688014 <+84>:    mov    %rdx,0x10(%rax)

70    res.current[3] = "";
   0x00007ffff4688018 <+88>:    mov    %rdx,0x18(%rax)

71    res.current[4] = "";
   0x00007ffff468801c <+92>:    mov    %rdx,0x20(%rax)

72  
73    res.cur_pre = "is";

74  
75    return res;
=> 0x00007ffff4688020 <+96>:    lea    0x14fe0(%rip),%rax        # 0x7ffff469d007
   0x00007ffff4688027 <+103>:   mov    %rax,0x10(%r12)
   0x00007ffff468802c <+108>:   lea    0x14fcd(%rip),%rax        # 0x7ffff469d000
   0x00007ffff4688033 <+115>:   mov    %rbx,(%r12)
   0x00007ffff4688037 <+119>:   mov    %rax,0x18(%r12)
   0x00007ffff468803c <+124>:   add    $0x8,%rsp
   0x00007ffff4688040 <+128>:   pop    %rbx
   0x00007ffff4688041 <+129>:   mov    %r12,%rax
   0x00007ffff4688044 <+132>:   pop    %r12
   0x00007ffff4688046 <+134>:   retq   
   0x00007ffff4688047:  nopw   0x0(%rax,%rax,1)

End of assembler dump.

अद्यतन 4 :

इसलिए, मानक के माध्यम से यहाँ पार्स करने की कोशिश कर रहे हैं, इसके कुछ भाग प्रासंगिक ( C11 ड्राफ्ट ) हैं:

6.3.2.3 Par7 रूपांतरण> अन्य ऑपरेंड> पॉइंटर्स

एक ऑब्जेक्ट प्रकार के लिए एक पॉइंटर को एक अलग ऑब्जेक्ट प्रकार के लिए एक पॉइंटर में परिवर्तित किया जा सकता है। यदि संदर्भित प्रकार के लिए परिणामी सूचक को सही ढंग से संरेखित नहीं किया गया है ), तो व्यवहार अपरिभाषित है।
अन्यथा, जब वापस फिर से परिवर्तित किया जाता है, तो परिणाम मूल सूचक के बराबर होगा। जब किसी ऑब्जेक्ट के पॉइंटर को पॉइंटर से कैरेक्टर टाइप में बदल दिया जाता है, तो परिणाम ऑब्जेक्ट के निम्नतम बाइट को इंगित करता है। परिणाम की क्रमिक वृद्धि, ऑब्जेक्ट के आकार तक, ऑब्जेक्ट के शेष बाइट्स को संकेत देता है।

6.5 Par6 अभिव्यक्तियाँ

किसी वस्तु का उसके संग्रहित मूल्य तक पहुँच के लिए प्रभावी प्रकार घोषित वस्तु का प्रकार है, यदि कोई हो। 87) यदि किसी वस्तु में एक संचित प्रकार होता है, जिसमें एक प्रकार का लवल्यू होता है, जिसमें एक प्रकार होता है, जो वर्ण प्रकार नहीं होता है, तो लैवल्यू का प्रकार उस पहुंच के लिए और बाद के अभिगम के लिए वस्तु का प्रभावी प्रकार बन जाता है, जो नहीं होता है संग्रहीत मान को संशोधित करें। यदि किसी ऑब्जेक्ट को किसी ऐसे प्रकार में कॉपी किया जाता है जिसमें मेम्पी या मेमोव का उपयोग करके कोई प्रकार नहीं घोषित किया जाता है, या उसे वर्ण प्रकार की एक सरणी के रूप में कॉपी किया जाता है, तो उस एक्सेस के लिए और बाद की एक्सेस के लिए संशोधित ऑब्जेक्ट का प्रभावी प्रकार मूल्य को संशोधित नहीं करता है। उस वस्तु का प्रभावी प्रकार जिससे मूल्य कॉपी किया जाता है, अगर उसमें एक है। किसी घोषित प्रकार वाले किसी अन्य एक्सेस के लिए, प्रभावी प्रकार का ऑब्जेक्ट केवल एक्सेस के लिए उपयोग किए जाने वाले अंतराल का प्रकार है।

87) आवंटित वस्तुओं का कोई घोषित प्रकार नहीं है।

IIUC R_allocएक mallocएड ब्लॉक में एक ऑफसेट देता है जिसे doubleसंरेखित करने की गारंटी है , और ऑफसेट के बाद ब्लॉक का आकार अनुरोधित आकार का है (आर विशिष्ट डेटा के लिए ऑफसेट से पहले आवंटन भी है)। वापसी पर R_allocइशारा करता है कि डाले (char *)

धारा 6.2.5 Par 29

शून्य के लिए एक पॉइंटर में एक चरित्र प्रकार के लिए एक संकेतक के रूप में एक ही प्रतिनिधित्व और संरेखण आवश्यकताएं होंगी। 48) इसी प्रकार, संगत प्रकारों के योग्य या अयोग्य संस्करणों की ओर संकेत करने वालों के लिए समान प्रतिनिधित्व और संरेखण आवश्यकताएं होंगी। संरचना प्रकार के सभी संकेत एक दूसरे के समान प्रतिनिधित्व और संरेखण आवश्यकताएं होंगी।
यूनियन प्रकार के सभी संकेत एक-दूसरे के समान प्रतिनिधित्व और संरेखण आवश्यकताओं होंगे।
अन्य प्रकार के पॉइंटर्स के लिए समान प्रतिनिधित्व या संरेखण आवश्यकताओं की आवश्यकता नहीं है।

४ align) समान प्रतिनिधित्व और संरेखण आवश्यकताओं का तात्पर्य कार्य करने के लिए विनिमेय asarguments का अर्थ है, फ़ंक्शंस से मान लौटना और यूनियनों के सदस्य हैं।

तो सवाल यह है कि "हम फिर से पात्र चयन करने की अनुमति है (char *)करने के लिए (const char **)और के रूप में यह करने के लिए लिखने (const char **)"। उपरोक्त का मेरा पठन यह है कि जब तक सिस्टम में चलने वाले कोड पर संरेखण के साथ संगत doubleसंरेखण है, तब तक ठीक है।

क्या हम "सख्त अलियासिंग" का उल्लंघन कर रहे हैं? अर्थात:

६.५ पार 7

एक वस्तु का अपना संग्रहीत मूल्य केवल एक लैवल्यू एक्सप्रेशन द्वारा एक्सेस किया जा सकता है, जिसमें निम्न प्रकार में से एक है: 88)

- वस्तु के प्रभावी प्रकार के साथ संगत एक प्रकार ...

88) इस सूची का आशय उन परिस्थितियों को निर्दिष्ट करना है जिनमें कोई वस्तु अलियास हो सकती है या नहीं।

तो, संकलक को क्या सोचना चाहिए कि वस्तु का प्रभावी प्रकार किसके द्वारा इंगित किया गया है res.target(या res.current) है? संभवतः घोषित प्रकार (const char **), या यह वास्तव में अस्पष्ट है? यह मुझे लगता है कि यह इस मामले में केवल इसलिए नहीं है क्योंकि इसमें कोई अन्य 'लैवल्यू' स्कोप नहीं है जो समान ऑब्जेक्ट को एक्सेस करता है।

मुझे लगता है मैं मानक के इन वर्गों से समझ निकालने के लिए संघर्ष कर रहा हूँ।


यदि पहले से ही जांच नहीं की गई है, तो यह देखने के लिए कि क्या किया जा रहा है, यह देखने के लिए बेचैनी को देखने लायक हो सकता है। और भी जीसीसी संस्करणों के बीच disassembly की तुलना करने के लिए।
kaylum

2
मैं जीसीसी के ट्रंक संस्करण के साथ गड़बड़ करने की कोशिश नहीं करूंगा। इसके साथ मस्ती करना अच्छा है, लेकिन इसे ट्रंक कहा जाता है। दुर्भाग्य से यह बताना लगभग असंभव है कि आपके कोड के बिना क्या गलत है (1) आपके कोड और सटीक कॉन्फ़िगरेशन (2) में समान आर्किटेक्चर पर समान जीसीसी संस्करण (3) है। अगर यह 10.0 से ट्रंक से स्थिर तक चलता है, तो मैं यह जाँचने का सुझाव दूंगा।
मार्को बोनेली

1
एक और टिप्पणी: -mtune=nativeविशेष सीपीयू के लिए अनुकूलन करता है जो आपकी मशीन के पास है। यह अलग-अलग परीक्षकों के लिए अलग होगा और इस मुद्दे का हिस्सा हो सकता है। यदि आप अपने साथ संकलन चलाते हैं, -vतो यह देखने में सक्षम होना चाहिए कि आपकी मशीन पर कौन सा सीपीयू परिवार है (जैसे -mtune=skylakeमेरे कंप्यूटर पर)।
नैट एल्ड्रेड 16

1
डिबग रन से बताने के लिए अभी भी मुश्किल है। अव्यवस्था निर्णायक होनी चाहिए। आपको कुछ भी निकालने की ज़रूरत नहीं है, बस .o फ़ाइल का उत्पादन करें जब आपने परियोजना को संकलित किया और इसे अलग कर दिया। आप disassemblegdb के अंदर निर्देश का उपयोग भी कर सकते हैं ।
नैट एल्ड्रेडगे

5
वैसे भी, बधाई हो, आप उन दुर्लभ लोगों में से एक हैं जिनकी समस्या वास्तव में एक कंपाइलर बग थी।
नैट एल्ड्रेड

जवाबों:


22

सारांश: यह जीसीसी में एक बग प्रतीत होता है, स्ट्रिंग अनुकूलन से संबंधित है। एक स्व-निहित टेस्टकेस नीचे है। शुरू में कुछ संदेह था कि क्या कोड सही है, लेकिन मुझे लगता है कि यह है।

मैंने बग की सूचना दी है PR 93982 केएक प्रस्तावित फिक्स किया गया था, लेकिन यह सभी मामलों में इसे ठीक नहीं करता है, जिसके परिणामस्वरूप फॉलोअप पीआर 94015 ( गॉडबोल्ट लिंक ) हो जाता है।

आपको ध्वज के साथ संकलन करके बग के चारों ओर काम करने में सक्षम होना चाहिए -fno-optimize-strlen


मैं आपके परीक्षण के मामले को निम्न न्यूनतम उदाहरण ( देवभूमि पर भी ) को कम करने में सक्षम था :

struct a {
    const char ** target;
};

char* R_alloc(void);

struct a foo(void) {
    struct a res;
    res.target = (const char **) R_alloc();
    res.target[0] = "12345678";
    res.target[1] = "";
    res.target[2] = "";
    res.target[3] = "";
    res.target[4] = "";
    return res;
}

Gcc ट्रंक (gcc संस्करण 10.0.1 20200225 (प्रायोगिक)) और -O2(अन्य सभी विकल्प अनावश्यक होने के कारण) के साथ, amd64 पर निर्मित असेंबली इस प्रकार है:

.LC0:
        .string "12345678"
.LC1:
        .string ""
foo:
        subq    $8, %rsp
        call    R_alloc
        movq    $.LC0, (%rax)
        movq    $.LC1, 16(%rax)
        movq    $.LC1, 24(%rax)
        movq    $.LC1, 32(%rax)
        addq    $8, %rsp
        ret

तो आप काफी सही हैं कि कंपाइलर इनिशियलाइज़ करने में असफल हो रहा है res.target[1](ध्यान दें कि यह स्पष्ट अनुपस्थिति है movq $.LC1, 8(%rax))।

कोड के साथ खेलना और "बग" को प्रभावित करना देखना दिलचस्प है। शायद काफी की वापसी प्रकार बदलने R_allocके लिएvoid * बनाता है इसे दूर जाना है, और आप "सही" विधानसभा उत्पादन देता है। शायद कम महत्वपूर्ण लेकिन अधिक मनोरंजक, स्ट्रिंग "12345678"को लंबे या छोटे होने के लिए बदलना भी इसे दूर कर देता है।


पिछली चर्चा, अब हल हो गई - कोड जाहिरा तौर पर कानूनी है।

मेरे पास सवाल यह है कि क्या आपका कोड वास्तव में कानूनी है। तथ्य यह है कि आप ले char *द्वारा दिया R_alloc()और करने के लिए इसे डाली const char **, और उसके बाद की दुकान एक const char *लगता है जैसे कि यह उल्लंघन कर सकते हैं सख्त अलियासिंग नियम , के रूप में charऔर const char *संगत प्रकार नहीं कर रहे हैं। एक अपवाद है जो आपको किसी भी वस्तु तक पहुंचने की अनुमति देता हैchar (जैसी चीजों को लागू करने के लिए memcpy), लेकिन यह दूसरा तरीका है, और जैसा कि मैं इसे समझता हूं, यह अनुमति नहीं है। यह आपके कोड को अपरिभाषित व्यवहार उत्पन्न करता है और इसलिए संकलक कानूनी रूप से वह कर सकता है जो वह चाहता है।

यदि ऐसा है, तो R के लिए सही कोड उनके कोड को बदलना होगा ताकि इसके बजाय R_alloc()रिटर्न void *हो char *। तब अलियासिंग की समस्या नहीं होगी। दुर्भाग्य से, वह कोड आपके नियंत्रण से बाहर है, और यह मेरे लिए स्पष्ट नहीं है कि आप सख्त अलियासिंग का उल्लंघन किए बिना इस फ़ंक्शन का उपयोग कैसे कर सकते हैं। वर्कअराउंड एक अस्थायी वैरिएबल को इंटर करने के लिए हो सकता है, उदाहरण के लिए, void *tmp = R_alloc(); res.target = tmp;जो परीक्षण के मामले में समस्या को हल करता है, लेकिन मुझे अभी भी यकीन नहीं है कि यह कानूनी है।

हालांकि, मैं इस "सख्त अलियासिंग" परिकल्पना के बारे में निश्चित नहीं हूं, क्योंकि संकलन -fno-strict-aliasing, जिसे AFAIK को ऐसे निर्माण की अनुमति देने के लिए माना जाता है, समस्या को दूर नहीं करता है!


अपडेट करें। कुछ विभिन्न विकल्पों की कोशिश कर रहा, मैंने पाया कि या तो -fno-optimize-strlenया -fno-tree-forwpropमें "सही" कोड जेनरेट किए जाने के परिणाम देगा। इसके अलावा, -O1 -foptimize-strlenगलत कोड का उपयोग करके (लेकिन -O1 -ftree-forwpropऐसा नहीं करता है)।

थोड़ी सी git bisectकवायद के बाद , यह त्रुटि 34fcf41e30ff56155e996f5x04 प्रतिबद्ध में पेश की गई लगती है


अद्यतन 2. मैंने एक छोटे से gcc स्रोत में खुदाई करने की कोशिश की, बस यह देखने के लिए कि मैं क्या सीख सकता हूं। (मैं किसी भी प्रकार के संकलक विशेषज्ञ होने का दावा नहीं करता!)

ऐसा लगता है कि कोड tree-ssa-strlen.cप्रोग्राम में दिखाई देने वाले तार का ट्रैक रखने के लिए है। जैसा कि निकट मैं बता सकता हूं, बग यह है कि बयान res.target[0] = "12345678";को देखने में कंपाइलर स्ट्रिंग के साथ स्ट्रिंग शाब्दिक का पता बताता "12345678"है। (जो इस संदिग्ध कोड से संबंधित लगता है जिसे पूर्वोक्त कमेटी में जोड़ा गया था, जहां यदि यह "स्ट्रिंग" के बाइट्स को गिनने की कोशिश करता है जो वास्तव में एक पता है, तो इसके बजाय यह देखता है कि वह पता किस ओर इशारा करता है।)

तो यह सोचता है कि बयान res.target[0] = "12345678", बजाय भंडारण के पते के "12345678"पते पर res.target, स्ट्रिंग उस पते पर भंडारण है ही, जैसे कि बयान थे strcpy(res.target, "12345678")। आगे के लिए ध्यान दें कि इससे पता चल जाएगा कि जाल नाल पते पर संग्रहीत किया जा रहा है res.target+8(कंपाइलर में इस स्तर पर, सभी ऑफ़सेट बाइट्स में हैं)।

अब जब संकलक दिखता है res.target[1] = "", तो यह इस तरह से व्यवहार करता है जैसे कि यह था strcpy(res.target+8, ""), 8 एक के आकार से आ रहा है char *। यह है, जैसे कि यह पते पर बस एक बाइट स्टोर कर रहे थे res.target+8। हालांकि, संकलक "जानता है" कि पिछले बयान ने पहले से ही उस पते पर एक न्यूल बाइट संग्रहीत किया था! जैसे, यह कथन "निरर्थक" है और इसे ( यहां ) खारिज किया जा सकता है ।

यह बताता है कि बग को ट्रिगर करने के लिए स्ट्रिंग को ठीक 8 वर्ण लंबा क्यों होना चाहिए। (हालांकि 8 के अन्य गुणक भी अन्य स्थितियों में बग को ट्रिगर कर सकते हैं।)


FWIW एक अलग प्रकार के पॉइंटर में पुन: पेश किया जाता है । मुझे यह पता करने के लिए एलियासिंग के बारे में नहीं पता है कि क्या यह ठीक है, int*लेकिन यह नहीं है const char**
ब्रोडीग

यदि सख्त अलियासिंग के बारे में मेरी समझ सही है, तो कास्ट int *भी गैरकानूनी है (या वास्तव में, वास्तव intमें अवैध भंडारण है)।
नैट एल्ड्रेडगे

1
यह सख्त अलियासिंग नियम से कोई लेना-देना नहीं है। सख्त अलियासिंग नियम उन डेटा तक पहुंचने के बारे में है जो आपने पहले से ही अलग-अलग हैंडल का उपयोग करके संग्रहीत किया है । जैसा कि आप केवल यहाँ असाइन करते हैं, यह सख्त अलियासिंग नियम को नहीं छूता है। कास्टिंग पॉइंटर तब मान्य होता है जब दोनों पॉइंटर प्रकारों में एक ही संरेखण आवश्यकताएं होती हैं, लेकिन यहां आप char*x86_64 से काम कर रहे हैं और काम कर रहे हैं ... मुझे यहां कोई यूबी नहीं दिख रहा है, यह जीसीसी बग है।
KamilCuk

1
हां और नहीं, @KamilCuk। मानक की शब्दावली में, "एक्सेस करना" में ऑब्जेक्ट के मूल्य को पढ़ना और संशोधित करना दोनों शामिल हैं। इसलिए सख्त अलियासिंग नियम "भंडारण" के लिए बोलता है। यह रीड-बैक ऑपरेशन तक सीमित नहीं है। लेकिन बिना किसी घोषित प्रकार वाली वस्तुओं के लिए, इस तथ्य से लूटा जाता है कि इस तरह की वस्तु को लिखने से जो लिखा गया था, उसके अनुरूप स्वचालित रूप से अपना प्रभावी प्रकार बदल जाता है। एक घोषित प्रकार के बिना ऑब्जेक्ट बिल्कुल गतिशील रूप से आवंटित वाले होते हैं (चाहे वे जिस प्रकार के पॉइंटर तक पहुंचते हैं), इसलिए वास्तव में यहां एसए उल्लंघन नहीं है।
जॉन बोलिंगर

2
हाँ, @, की परिभाषा के साथ R_alloc(), कार्यक्रम अनुरूप है, जिसमें अनुवाद इकाई R_alloc()को परिभाषित किया गया है। यह संकलक है जो यहाँ अनुरूप होने में विफल रहता है।
जॉन बोलिंगर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.