LINUX.ORG.RU

pointer aliasing & UB

 


1

3

Допустим, мы знаем layout (т.е. количество полей и тип каждого поля) сишной структуры, но не знаем имена полей.

Нужно сгенерировать сишный код, который:

1. создаст экземпляр структуры, заполнит поля определёнными данными (нет, struct S s = {1, 2, 3}; писать нельзя, по определённым причинам);

2. считает значения полей существующей структуры.

Допустим, известно, что это struct S и у неё три поля типа int.

Втупую:

struct S_compat { int a0; int a1; int a2; } s_compat;
s_compat.a0 = /* ... */;
s_compat.a1 = /* ... */;
s_compat.a2 = /* ... */;

struct S s;
* (struct S_compat *) &s = s_compat;

Насколько я понимаю, здесь UB из-за pointer aliasing. Так ли это?

Далее:

struct S_compat { int a0; int a1; int a2; } s_compat;
s_compat.a0 = /* ... */;
s_compat.a1 = /* ... */;
s_compat.a2 = /* ... */;

struct S s;
memcpy(&s, &s_compat, sizeof(struct S));

Есть ли здесь UB? Если да, то как это сделать правильно?

Если можно, приведите ссылки на релевантные места стандарта. Спасибо.

P.S. про -fno-strict-aliasing, __attribute((__may_alias__)) знаю, хотелось бы в рамках стандарта.



Последнее исправление: shdown (всего исправлений: 1)

Да, здесь UB. Да, через memcpy. См. 6.5 Expressions 7

alexanius ★★
()

Там единственное UB — выравнивание! Оригинальная структура могла быть упакованной либо выравненной на 2 байта, а ты выравниваешь свою структуру на 4 байта и получаешь треш какой-то вместо данных.

И как ты вообще себе можешь представить, что у тебя есть бинарный блоб-структура, структура которого неизвестна (звиняй за тавтологию)?

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

Интуитивное понимание UB иногда подводит, Эдди. Лучше смотреть в стандарт.

Структура известна — неизвестны имена переменных.

shdown
() автор топика
Последнее исправление: shdown (всего исправлений: 1)
Ответ на: комментарий от shdown

А, я просто не очень в курсе, как копирование структур происходит — я крайне редко таким страдаю. Обычно работаю с указателями или использую memcpy.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от CatsCantFly

Через memcpy - нормально, UB нет.

proof?

В плюсах через memcpy тоже UB, если структура не standard layout

И когда standard layout — UB.

anonymous
()

во втором случае ub нет, но содержимое s может быть каким угодно (в зависимости от версии компилятора, флагов, и т.д.). memcpy (7.24.2.1 в последнем стандарте) предусматривает ub только для пересекающихся областей памяти (к слову, memmove вообще не предусматривает ub).

anonymous
()
Ответ на: комментарий от anonymous

Структуры S и S_compat имеют одинаковый layout и, соответственно, одинаковый размер. Почему «содержимое s может быть каким угодно»?

shdown
() автор топика
Ответ на: комментарий от anonymous

да, выравнивание может быть разное.

Нет, не может — layout одинаковый.

> > а не проще s=s_compat ?
> Не проще, ибо не скомпилируется.
*(s_compat*)a={1,2,3};

Зачем вы подменяете то, про что мы говорим? Я говорил, что не скомпилируется “s=s_compat”.

В c++

А у меня в тегах — Си.

заработает.

Нет, не заработает, ибо UB. Заработать оно может только из-за благоприятного стечения обстоятельств. Стандарт первичен, реализация вторична.

shdown
() автор топика
Ответ на: комментарий от anonymous

Это неправда. На анонимные комментарии в этом треде далее отвечаю только, если они по делу.

shdown
() автор топика

Насколько я понимаю, здесь UB из-за pointer aliasing. Так ли это?

Это не UB, а ошибка компиляции, так как struct S в приведённом тексте не определён. Если структура всё-таки где-то определена (и не показана в снипете), то зависит от её совместимости (compatible type) с оригинальной (если совместима, то ок). Это если рассматривать работу в рамках одного translation unit. Если структура (точнее массив байтов) передаётся в программу «извне» (по памяти, по сокету, по сети), то ещё зависит от того, как эта структура компилировалась другим компилятором.

Есть ли здесь UB? Если да, то как это сделать правильно?

После memcpy непосредственно нет, так как это побайтовое копирование. UB может быть при обращении к s по своему типу (trap representation), что во многом обессмысливает копирование. Другими словами, в Си можно побайтово копировать типы (есть даже явное разрешение при приведении к unsigned char*), но юзать нельзя в общем случае.

Если можно, приведите ссылки на релевантные места стандарта. Спасибо.

Это в общих местах (что такое тип, область определения и время жизни) 6 раздела про типы (точно не помню, последний раз открывал лет 5 назад).

P. S.

А на самом деле проблема начинается здесь:

Допустим, мы знаем layout ...

Мы можем знать layout типа если он внутри одного translation unit. А если это так, то нам доступны имена переменных, если они намеренно не скрыты, но это проблема дизайна (не надо играть в игры по сокрытию членов структур и последующим побайтовым чтением).

mxfm ★★
()

Первый вариант очевидно UB.

А насчёт второго... На основании древних ответов сишного комитета под некоторыми defect report-ами, просмотра некоторых пропозалов к C2x а также мнения некоторых людей которые «в теме», я бы сказал что на твой второй вопрос на основании стандарта нельзя дать определённый ответ. Никто его не знает.

Считай оба варианта UB. Самый безопасный вариант, если тебя интересует чисто формальная сторона.

anonymous
()
Ответ на: комментарий от CatsCantFly

Через memcpy - нормально, UB нет.

Да что ты говоришь! Я выше про выравнивание уже говорил!!!

Eddy_Em ☆☆☆☆☆
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.