मेरे पास सी संकलित कोड के साथ एक आर पैकेज है जो काफी समय से अपेक्षाकृत स्थिर है और अक्सर प्लेटफ़ॉर्म और कंपाइलरों (विंडोज़ / ओएक्सएक्स / डेबियन / फेडोरा जीसीसी / क्लैंग) की एक विस्तृत विविधता के खिलाफ परीक्षण किया जाता है।
हाल ही में पैकेज का परीक्षण करने के लिए एक नया प्लेटफ़ॉर्म जोड़ा गया:
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-base
docker कंटेनर का उपयोग करके लगातार segfault को पुन: पेश करने में सक्षम रहा हूं । कम अनुकूलन चलाने से समस्या से छुटकारा मिलता है। Valgrind (-O0 और -O2), UBSAN (gcc / clang) सहित किसी भी अन्य सेट-अप को चलाने से कोई समस्या नहीं होती है। मुझे भी यकीन है कि यह भाग गया था , लेकिन डेटा नहीं है।gcc-10.0.1
-O2
gcc-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 **)
, या यह वास्तव में अस्पष्ट है? यह मुझे लगता है कि यह इस मामले में केवल इसलिए नहीं है क्योंकि इसमें कोई अन्य 'लैवल्यू' स्कोप नहीं है जो समान ऑब्जेक्ट को एक्सेस करता है।
मुझे लगता है मैं मानक के इन वर्गों से समझ निकालने के लिए संघर्ष कर रहा हूँ।
-mtune=native
विशेष सीपीयू के लिए अनुकूलन करता है जो आपकी मशीन के पास है। यह अलग-अलग परीक्षकों के लिए अलग होगा और इस मुद्दे का हिस्सा हो सकता है। यदि आप अपने साथ संकलन चलाते हैं, -v
तो यह देखने में सक्षम होना चाहिए कि आपकी मशीन पर कौन सा सीपीयू परिवार है (जैसे -mtune=skylake
मेरे कंप्यूटर पर)।
disassemble
gdb के अंदर निर्देश का उपयोग भी कर सकते हैं ।