Вызывает ли сравнение UB свободный указатель? - PullRequest
0 голосов
/ 03 октября 2018

Это довольно распространенный шаблон, например, в hexchat (может не скомпилироваться, см. Также плагин docs . Также обратите внимание, что hexchat_plugin_get_info не использовался вечно, поэтому я опускаю его дляпростота):

static hexchat_plugin *ph;
static int timer_cb(void *userdata) {
    if (hexchat_set_context(ph, userdata)) { /* <-- is this line UB? */
        /* omitted */
    }
    return 0;
}
static int do_ub(char *word[], char *word_eol[], void *userdata) {
    void *context = hexchat_get_context(ph);
    hexchat_hook_timer(ph, 1000, timer_cb, context);
    hexchat_command(ph, "close"); /* free the context - in practice this would be done by another plugin or by the user, not like this, but for the purposes of this example this simulates the user closing the context. */
    return HEXCHAT_EAT_ALL;
}
int hexchat_plugin_init(hexchat_plugin *plugin_handle, char **plugin_name, char **plugin_desc, char **plugin_version, char *arg) {
    *plugin_name = "do_ub";
    *plugin_desc = "does ub when you /do_ub";
    *plugin_version = "1.0.0";
    ph = plugin_handle;
    /* etc */
    hexchat_hook_command(ph, "do_ub", 0, do_ub, "does UB", NULL);
    return 1;
}

Строка в timer_cb заставляет hexchat сравнивать указатель (потенциально свободный - обязательно свободный в этом примере, см. комментарий в do_ub) с другим указателем,если вы следуете из здесь (plugin.c # L1089, hexchat_set_context) вы окажетесь здесь (hexchat.c # L191, is_session) .Чтобы вызвать этот код, запустите /do_ub в hexchat.

Соответствующий код:

int
hexchat_set_context (hexchat_plugin *ph, hexchat_context *context)
{
    if (is_session (context))
    {
        ph->context = context;
        return 1;
    }
    return 0;
}

int
is_session (session * sess)
{
    return g_slist_find (sess_list, sess) ? 1 : 0;
}

Это UB?

Ответы [ 3 ]

0 голосов
/ 03 октября 2018

Да, использование значения указателя, которое было освобождено для чего угодно - даже на первый взгляд безобидное сравнение - строго говоря, неопределенное поведение.На практике это вряд ли вызовет какие-либо реальные проблемы, но я бы сказал, что этого стоит избегать.

См. Также список C FAQ , вопрос 7.21 .

0 голосов
/ 03 октября 2018

tl; dr: Возможность выполнения определенных операций, таких как сравнение указателей, без учета времени жизни идентифицированных объектов, является популярным расширением, которое подавляющее большинство компиляторов можно настроить для поддержки с отключенными оптимизациями.Однако его поддержка не является обязательной для Стандарта, и агрессивные оптимизаторы могут нарушать код, который на него опирается.

Когда был написан Стандарт, существовало несколько платформ с сегментированной памятью, где пытались загрузитьуказатель на регистры приведет к тому, что система получит информацию об области памяти, в которой находится указатель.Если такая информация больше недоступна, попытка ее получения может иметь произвольные последствия за пределами юрисдикции Стандарта.Если бы стандарт требовал, чтобы сравнения, включающие такие указатели, не имели побочных эффектов, кроме уступки 0 или 1, это сделало бы язык непрактичным на таких платформах.

Хотя авторы стандарта, без сомнения, знали, что возможность использоватьСравнение с произвольными указателями (при условии, что результаты могут быть не особенно значимыми) было полезной функцией, поддерживаемой каждой реализацией, нацеленной на обычное оборудование, они не видели необходимости рассматривать ее как нечто большее, чем «популярное расширение», которое поддерживают реализации качества.всякий раз, когда это будет полезно и практично.

Из Обоснования C89, стр.11 строка 23:

Термины неопределенное поведение, неопределенное поведение и поведение, определяемое реализацией, используются дляклассифицировать результаты написания программ, свойства которых Стандарт не описывает или не может полностью описать.Цель принятия этой категоризации состоит в том, чтобы обеспечить определенное разнообразие среди реализаций, которое позволяет качеству реализации быть активной силой на рынке, а также разрешить определенные популярные расширения, не удаляя кэш соответствия стандарта.В информативном Приложении J к Стандарту приводится каталог поведения, подпадающего под одну из этих трех категорий.

К сожалению, хотя почти все платформы, используемые сегодня, могут поддерживать такую ​​семантику практически без затрат, некоторые авторы компиляторов считают, чтоих желание предположить, что код никогда не будет делать что-либо с освобожденными указателями, более важными, чем любое значение, которое программисты могли бы получить от того, что было по существу универсально поддерживаемым расширением на обычных платформах.Если никто не может гарантировать, что любой, использующий свой код, отключит фальшивые «оптимизации», навязанные авторами чрезмерно активных оптимизаторов, которые стремятся избавить язык от полезных расширений, возможно, придется написать дополнительный код, чтобы обойти отсутствие таких расширений..

0 голосов
/ 03 октября 2018

Использование значения указателя после того, как объект, на который он указывает, достигнет конца жизни, равно неопределенно , как указано в черновом варианте C11 Standard 6.2.4p2 (Длительность хранения объектов) (акцент мой):

Время жизни объекта - это часть выполнения программы, в течение которой для него гарантированно сохраняется хранилище.Объект существует, имеет постоянный адрес и сохраняет свое последнее сохраненное значение в течение всего времени жизни.Если на объект ссылаются вне его времени жизни, поведение не определено. Значение указателя становится неопределенным , когда объект, на который он указывает (или просто прошедший), достигает конца своего времени жизни.

И использование его значения (просто для чего угодно)явное неопределенное поведение , как указано в Приложении J.2 (неопределенное поведение):

Поведение не определено в следующих обстоятельствах: [...] Используется значение указателя на объект, срок жизни которого истек (6.2.4).

...