हां, __attribute__((packed))
कुछ प्रणालियों पर संभावित रूप से असुरक्षित है। लक्षण शायद x86 पर दिखाई नहीं देगा, जो समस्या को और अधिक कपटी बनाता है; x86 सिस्टम पर परीक्षण समस्या को प्रकट नहीं करेगा। (X86 पर, गलत तरीके से एक्सेस को हार्डवेयर में संभाला जाता है; यदि आप एक int*
पॉइंटर को डिसेंट करते हैं जो एक अजीब पते की ओर इशारा करता है, तो यह थोड़ा धीमा होगा अगर यह ठीक से गठबंधन किया गया था, लेकिन आपको सही परिणाम मिलेगा।)
कुछ अन्य प्रणालियों पर, जैसे कि SPARC, एक गलत int
वस्तु तक पहुँचने का प्रयास एक बस त्रुटि का कारण बनता है, जिससे प्रोग्राम क्रैश हो जाता है।
ऐसी प्रणालियाँ भी आई हैं, जहाँ गलत तरीके से पहुंच पता चुपचाप पते के कम-क्रम बिट्स को अनदेखा कर देती है, जिससे यह मेमोरी के गलत भाग तक पहुँच सकती है।
निम्नलिखित कार्यक्रम पर विचार करें:
#include <stdio.h>
#include <stddef.h>
int main(void)
{
struct foo {
char c;
int x;
} __attribute__((packed));
struct foo arr[2] = { { 'a', 10 }, {'b', 20 } };
int *p0 = &arr[0].x;
int *p1 = &arr[1].x;
printf("sizeof(struct foo) = %d\n", (int)sizeof(struct foo));
printf("offsetof(struct foo, c) = %d\n", (int)offsetof(struct foo, c));
printf("offsetof(struct foo, x) = %d\n", (int)offsetof(struct foo, x));
printf("arr[0].x = %d\n", arr[0].x);
printf("arr[1].x = %d\n", arr[1].x);
printf("p0 = %p\n", (void*)p0);
printf("p1 = %p\n", (void*)p1);
printf("*p0 = %d\n", *p0);
printf("*p1 = %d\n", *p1);
return 0;
}
Gcc 4.5.2 के साथ x86 उबंटू पर, यह निम्नलिखित आउटपुट का उत्पादन करता है:
sizeof(struct foo) = 5
offsetof(struct foo, c) = 0
offsetof(struct foo, x) = 1
arr[0].x = 10
arr[1].x = 20
p0 = 0xbffc104f
p1 = 0xbffc1054
*p0 = 10
*p1 = 20
SPARC Solaris 9 पर gcc 4.5.1 के साथ, यह निम्नलिखित उत्पादन करता है:
sizeof(struct foo) = 5
offsetof(struct foo, c) = 0
offsetof(struct foo, x) = 1
arr[0].x = 10
arr[1].x = 20
p0 = ffbff317
p1 = ffbff31c
Bus error
दोनों ही मामलों में, इस कार्यक्रम को बिना किसी अतिरिक्त विकल्प के संकलित किया गया है gcc packed.c -o packed
।
(एक प्रोग्राम जो सरणी के बजाय एकल संरचना का उपयोग करता है, समस्या को मज़बूती से प्रदर्शित नहीं करता है, क्योंकि कंपाइलर एक विषम पते पर संरचना को आवंटित कर सकता है ताकि x
सदस्य ठीक से गठबंधन हो। दो struct foo
वस्तुओं की एक सरणी के साथ , कम से कम एक या दूसरे। एक गलत x
सदस्य होगा।)
(इस मामले में, p0
एक गलत पते की ओर इशारा करता है, क्योंकि यह एक int
सदस्य के बाद एक पैक किए गए सदस्य को इंगित करता है char
। p1
सही ढंग से गठबंधन करने के लिए होता है, क्योंकि यह सरणी के दूसरे तत्व में एक ही सदस्य को इंगित करता है, इसलिए char
इससे पहले दो ऑब्जेक्ट हैं। - और स्पार्क सोलारिस पर सरणी arr
को एक पते पर आवंटित किया गया प्रतीत होता है जो समान है, लेकिन 4. का एक से अधिक नहीं है।)
जब x
किसी सदस्य के struct foo
नाम का जिक्र किया जाता है, तो संकलक जानता है कि x
संभावित रूप से गलत संकेत दिया गया है, और इसे सही तरीके से एक्सेस करने के लिए अतिरिक्त कोड उत्पन्न करेगा।
एक बार जब पता arr[0].x
या arr[1].x
एक पॉइंटर ऑब्जेक्ट में संग्रहीत किया गया है, तो न तो संकलक और न ही चल रहे कार्यक्रम को पता है कि यह एक गलत int
ऑब्जेक्ट को इंगित करता है । यह सिर्फ यह मानता है कि यह ठीक से संरेखित है, जिसके परिणामस्वरूप (कुछ प्रणालियों पर) बस त्रुटि या इसी तरह की अन्य विफलता है।
इसे gcc में फिक्स करना, मेरा मानना है कि अव्यावहारिक है। एक सामान्य समाधान के लिए, गैर-तुच्छ संरेखण आवश्यकताओं के साथ किसी भी प्रकार के लिए एक पॉइंटर को रोकने के लिए प्रत्येक प्रयास की आवश्यकता होगी (क) संकलन समय पर यह साबित करना कि पॉइंटर एक भरे हुए संरचना के गलत सदस्य को इंगित नहीं करता है, या (बी) बल्कियर और धीमे कोड उत्पन्न करना जो संरेखित या गलत वस्तुओं को संभाल सकता है।
मैंने एक gcc बग रिपोर्ट प्रस्तुत की है । जैसा कि मैंने कहा, मुझे विश्वास नहीं है कि इसे ठीक करना व्यावहारिक है, लेकिन प्रलेखन को इसका उल्लेख करना चाहिए (यह वर्तमान में नहीं है)।
अद्यतन : 2018-12-20 के रूप में, यह बग FIXED के रूप में चिह्नित है। पैच -Waddress-of-packed-member
डिफ़ॉल्ट रूप से सक्षम एक नए विकल्प के अलावा के साथ gcc 9 में दिखाई देगा ।
जब स्ट्रक्चर या यूनियन के पैक्ड सदस्य का पता लिया जाता है, तो इसका परिणाम अनलॉग्ड पॉइंटर मान हो सकता है। यह पैच जोड़ता है -Waddress-of-पैक-मेंबर को पॉइंटर असाइनमेंट पर अलाइनमेंट की जाँच करने के लिए और अन-असाइन किए गए प्वॉइंट के साथ-साथ अनलॉग्ड पाइंटर को चेतावनी देने के लिए।
मैं सिर्फ स्रोत से जीसीसी के उस संस्करण का निर्माण किया है। उपरोक्त कार्यक्रम के लिए, यह इन निदानों का उत्पादन करता है:
c.c: In function ‘main’:
c.c:10:15: warning: taking address of packed member of ‘struct foo’ may result in an unaligned pointer value [-Waddress-of-packed-member]
10 | int *p0 = &arr[0].x;
| ^~~~~~~~~
c.c:11:15: warning: taking address of packed member of ‘struct foo’ may result in an unaligned pointer value [-Waddress-of-packed-member]
11 | int *p1 = &arr[1].x;
| ^~~~~~~~~