Ya, __attribute__((packed))berpotensi tidak aman di beberapa sistem. Gejala ini mungkin tidak akan muncul di x86, yang hanya membuat masalah lebih berbahaya; pengujian pada sistem x86 tidak akan mengungkapkan masalah. (Pada x86, akses yang tidak selaras ditangani dalam perangkat keras; jika Anda men-referensi int*pointer yang menunjuk ke alamat ganjil, itu akan sedikit lebih lambat daripada jika itu benar selaras, tetapi Anda akan mendapatkan hasil yang benar.)
Pada beberapa sistem lain, seperti SPARC, mencoba mengakses intobjek yang tidak selaras menyebabkan kesalahan bus, menabrak program.
Ada juga sistem di mana akses yang tidak selaras diam-diam mengabaikan bit urutan rendah dari alamat, menyebabkannya mengakses potongan memori yang salah.
Pertimbangkan program berikut:
#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;
}
Pada x86 Ubuntu dengan gcc 4.5.2, ia menghasilkan output berikut:
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
Pada SPARC Solaris 9 dengan gcc 4.5.1, ini menghasilkan yang berikut:
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
Dalam kedua kasus, program dikompilasi tanpa opsi tambahan, adil gcc packed.c -o packed.
(Program yang menggunakan struct tunggal daripada array tidak menunjukkan masalah, karena kompiler dapat mengalokasikan struct pada alamat ganjil sehingga xanggota disejajarkan dengan benar. Dengan array dua struct fooobjek, setidaknya satu atau yang lain akan memiliki anggota yang tidak selaras x.)
(Dalam hal ini, p0menunjuk ke alamat yang tidak selaras, karena menunjuk ke anggota yang dikemas intmengikuti charanggota. p1Kebetulan disejajarkan dengan benar, karena menunjuk ke anggota yang sama dalam elemen kedua array, sehingga ada dua charobjek sebelumnya - dan pada SPARC Solaris, array arrtampaknya dialokasikan pada alamat yang genap, tetapi bukan kelipatan 4.)
Ketika merujuk ke xanggota struct foodengan nama, kompiler tahu bahwa xberpotensi berpotensi tidak selaras, dan akan menghasilkan kode tambahan untuk mengaksesnya dengan benar.
Setelah alamat arr[0].xatau arr[1].xtelah disimpan dalam objek pointer, baik kompiler maupun program yang berjalan tidak tahu bahwa itu menunjuk ke intobjek yang tidak selaras . Itu hanya mengasumsikan bahwa itu benar selaras, menghasilkan (pada beberapa sistem) kesalahan bus atau kegagalan lainnya yang serupa.
Memperbaiki ini dalam gcc, saya percaya, tidak praktis. Sebuah solusi umum akan membutuhkan, untuk setiap upaya untuk melakukan dereferensi pointer ke jenis apa pun dengan persyaratan penyelarasan non-trivial baik (a) membuktikan pada waktu kompilasi bahwa pointer tidak menunjuk ke anggota yang tidak selaras dari struct yang dikemas, atau (b) menghasilkan kode bulkier dan lebih lambat yang dapat menangani objek yang disejajarkan atau tidak selaras.
Saya telah mengirimkan laporan bug gcc . Seperti yang saya katakan, saya tidak percaya itu praktis untuk memperbaikinya, tetapi dokumentasi harus menyebutkannya (saat ini tidak).
UPDATE : Pada 2018-12-20, bug ini ditandai sebagai TETAP. Tambalan akan muncul di gcc 9 dengan penambahan -Waddress-of-packed-memberopsi baru , diaktifkan secara default.
Ketika alamat anggota struct atau gabungan yang diambil diambil, itu dapat menghasilkan nilai penunjuk yang tidak selaras. Patch ini menambahkan -Waddress-of-packed-member untuk memeriksa perataan pada penugasan pointer dan memperingatkan alamat yang tidak selaras serta pointer yang tidak selaras
Saya baru saja membangun versi gcc dari sumber. Untuk program di atas, ini menghasilkan diagnostik ini:
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;
| ^~~~~~~~~