Почему разработчики переписали копирование строк и что это меняет для безопасности.

Есть функции, которые в языке C выглядят как удобные мелочи, но на практике годами собирают вокруг себя ошибки. В проекте curl одну из таких уже «вынесли» из кода, а теперь решили добить и вторую. Разработчик curl Даниэль Стенберг рассказал, почему команда полностью отказалась от strcpy и чем заменяет привычное копирование строк.
Раньше curl уже прошел похожую чистку. В проекте избавились от всех вызовов strncpy. У этой функции, по словам Стенберга, слишком коварный характер: она может не поставить завершающий ноль в конце строки и при этом заполняет буфер нулями до конца, что часто приводит к неожиданным последствиям. В большинстве кодовых баз, считает он, каждый вызов strncpy потенциально является ошибкой. При переписывании команда придерживалась простого правила: либо копируем строку целиком и корректно, либо возвращаем ошибку. Частичное копирование редко бывает правильным выбором, а если вдруг нужно именно оно, это проще и честнее сделать через memcpy и явно поставить нулевой байт в нужном месте.
С strcpy ситуация тоньше. У нее, как ни странно, более понятный интерфейс, и у функции есть легитимные сценарии применения. Проблема в другом: strcpy не принимает размер буфера назначения и не знает длину исходной строки. В идеальном мире это не страшно, потому что в C ее используют только там, где разработчик полностью контролирует и источник, и приемник. Но «в идеальном мире» и «всегда» не одно и то же. Ошибаются все, а код в таких проектах живет десятилетиями, постоянно меняется и обрастает правками разных людей.
И вот здесь появляется главная угроза. Использование strcpy почти всегда означает, что где-то раньше по коду уже были проверки размеров, которые гарантируют безопасность копирования. Если все сделано аккуратно, между проверкой и самой копией ничего не успевает измениться. Но в реальном проекте со временем эти части кода могут «разъехаться»: проверки перенесут, условия поменяют, добавят новую ветку, и в какой-то момент вызов strcpy окажется слишком далеко от того места, где изначально подтверждалось, что места хватает. Чем больше дистанция между проверкой и копированием, тем выше шанс, что по дороге кто-то невольно сломает гарантию.
Чтобы не зависеть от дисциплины авторов и не надеяться на внимательность при будущих правках, в curl ввели собственную замену копированию строк. Она требует сразу все данные, которые обычно «предполагаются» при strcpy: буфер назначения, его размер, источник и длину исходной строки. Копирование выполняется только если гарантированно помещаются и символы, и завершающий ноль. Реализацию сделали через memcpy, а сам strcpy теперь можно просто запретить на уровне проекта, как раньше запретили strncpy.
Да, новый вариант более громоздкий и требует от программиста чуть больше усилий: нужно знать длину строки и передавать размер буфера. Но команда считает, что эта цена оправдана. Так проверка и действие оказываются связаны в одном вызове, и их нельзя случайно развести по разным местам при следующей волне рефакторинга.
Стенберг добавил и забавный побочный эффект. В коде больше не останется «приманки» для ИИ-ботов, которые любят находить strcpy в репозитории и уверенно заявлять про «уязвимость», даже когда копирование корректно защищено проверками. Впрочем, он тут же признает, что это не панацея: если убрать один повод для галлюцинаций, генеративные модели придумают другой. С этой «кашицей из ИИ-отчетов» все равно нельзя выиграть окончательно.
Идея в итоге простая: не спорить с человеческим фактором и не рассчитывать, что все будущие изменения будут аккуратными, а встроить безопасность прямо в интерфейс функции. В curl уже проверили это на strncpy, теперь очередь дошла и до strcpy.