Go, Обратная совместимость и GODEBUG
Введение
Акцент Go на обратной совместимости является одним из его ключевых преимуществ. Однако бывают случаи, когда мы не можем обеспечить полную совместимость. Если код зависит от ошибочного (включая небезопасное) поведения, то исправление ошибки приведет к поломке такого кода. Новые функции также могут иметь аналогичное влияние: включение использования HTTP/2 клиентом HTTP сломало программы, подключающиеся к серверам с ошибочными реализациями HTTP/2. Такие изменения неизбежны и разрешены правилами совместимости Go 1. Тем не менее, Go предоставляет механизм под названием GODEBUG, который позволяет снизить влияние таких изменений на разработчиков Go, использующих более новые инструменты компиляции для старого кода.
Настройка GODEBUG представляет собой пару ключ=значение,
которая управляет выполнением определенных частей программы на Go.
Переменная окружения GODEBUG
может содержать список этих настроек, разделенных запятыми.
Например, если программа на Go запущена в среде, содержащей
<code>GODEBUG=http2client=0,http2server=0 </code>
то эта программа по умолчанию отключит использование HTTP/2 как в клиенте HTTP, так и в сервере HTTP.
Неизвестные настройки в переменной окружения GODEBUG игнорируются.
Также возможно установить значение GODEBUG по умолчанию для конкретной программы
(обсуждается ниже).
При подготовке любого изменения, разрешенного правилами совместимости Go 1, но которое может все же сломать некоторые существующие программы, мы сначала проектируем изменение так, чтобы максимально сохранить работоспособность существующих программ. Для оставшихся программ мы определяем новую настройку GODEBUG, которая позволяет отдельным программам снова вернуться к старому поведению. Настройка GODEBUG не может быть добавлена, если это невозможно, но такое должно быть крайне редким случаем.
Настройки GODEBUG, добавленные ради совместимости, будут поддерживаться
минимум два года (четыре релиза Go).
Некоторые, такие как http2client и http2server,
будут поддерживаться гораздо дольше, даже бессрочно.
По возможности, каждая настройка GODEBUG имеет связанный
runtime/metrics счетчик
с именем /godebug/non-default-behavior/<name>:events,
который подсчитывает количество раз, когда конкретная программа
изменила свое поведение из-за нестандартного значения этой настройки.
Например, когда установлена GODEBUG=http2client=0,
счетчик /godebug/non-default-behavior/http2client:events
подсчитывает количество HTTP-транспортов, которые программа
настроила без поддержки HTTP/2.
Значения GODEBUG по умолчанию
Когда настройка GODEBUG не указана в переменной окружения,
её значение выводится из трёх источников:
значения по умолчанию для используемого инструментария Go,
дополненные версией Go, указанной в go.mod,
а затем переопределенные явными строками //go:debug в программе.
История GODEBUG содержит точные значения по умолчанию для каждой версии инструментария Go.
Например, Go 1.21 вводит настройку panicnil,
контролирующую, разрешено ли использовать panic(nil);
по умолчанию она равна panicnil=0, что делает panic(nil) ошибкой времени выполнения.
Использование panicnil=1 восстанавливает поведение Go 1.20 и более ранних версий.
При компиляции рабочего модуля или рабочей среды, которые объявляют более старую версию Go, инструментарий Go корректирует свои значения по умолчанию так, чтобы они максимально соответствовали этой более старой версии Go. Например, когда инструментарий Go 1.21 компилирует программу, если в go.mod рабочего модуля или в go.work рабочей среды указано go 1.20, то программа будет использовать значение по умолчанию panicnil=1, соответствующее Go 1.20, а не Go 1.21.
Поскольку этот способ установки значений по умолчанию GODEBUG был введён только в Go 1.21, программы, указывающие версии Go ранее Go 1.20, настроены так, чтобы соответствовать Go 1.20, а не более старой версии.
Чтобы переопределить эти значения по умолчанию, начиная с Go 1.23, в go.mod рабочего модуля или в go.work рабочей среды можно указать одну или несколько строк godebug:
<code>godebug ( default=go1.21 panicnil=1 asynctimerchan=0 ) </code>
Специальный ключ default указывает версию Go, с которой нужно брать неуказанные параметры. Это позволяет установить значения по умолчанию GODEBUG отдельно от версии языка Go в модуле. В данном примере программа запрашивает семантику Go 1.21, а затем запрашивает поведение panic(nil) до Go 1.21 и новое поведение asynctimerchan=0 из Go 1.23.
Только go.mod рабочего модуля учитывается при обработке директив godebug. Любые директивы в зависимых модулях игнорируются. Является ошибкой указывать godebug с неизвестным параметром. (Инструментарии, более старые чем Go 1.23, отвергают все строки godebug, поскольку они вообще не понимают godebug.) Если используется рабочая среда, директивы godebug в файлах go.mod игнорируются, и вместо этого будет использоваться go.work.
Значения по умолчанию из строк go и godebug применяются ко всем основным пакетам, которые собираются. Для более точного контроля, начиная с Go 1.21, исходные файлы основного пакета могут содержать одну или несколько директив //go:debug в начале файла (до инструкции package). Строки godebug из предыдущего примера будут выглядеть так:
<code>//go:debug default=go1.21 //go:debug panicnil=1 //go:debug asynctimerchan=0 </code>
Начиная с Go 1.21, инструментарий Go рассматривает директиву //go:debug с неизвестным параметром GODEBUG как недопустимую программу. Программы с более чем одной директивой //go:debug для одного и того же параметра также считаются недопустимыми. (Старые инструментарии полностью игнорируют директивы //go:debug.)
Значения по умолчанию, которые будут скомпилированы в основной пакет, можно получить с помощью команды:
<code>go list -f '{{.DefaultGODEBUG}}' my/main/package
</code>
Отображаются только отличия от базовых значений по умолчанию инструментария Go.
При тестировании пакета директивы //go:debug в файлах *_test.go рассматриваются как директивы для основного пакета теста. В любом другом контексте директивы //go:debug игнорируются инструментарием; go vet сообщает об таких строках как о неправильно расположенных.
История GODEBUG
В этом разделе описаны настройки GODEBUG, которые были добавлены или удалены в каждой основной версии Go с целью обеспечения совместимости. Пакеты или программы могут определять дополнительные настройки для внутренней отладки; например, см. документацию среды выполнения и документацию команды go.
Go 1.26
В Go 1.26 была добавлена новая настройка httpcookiemaxnum, которая контролирует максимальное количество
cookies, которые пакет net/http будет принимать при разборе HTTP заголовков. Если количество
cookies в заголовке превышает значение, установленное в httpcookiemaxnum, разбор cookies
будет завершён преждевременно. Значение по умолчанию — httpcookiemaxnum=3000. Установка
httpcookiemaxnum=0 позволит разбору cookies принимать неограниченное количество cookies.
Для предотвращения атак типа «отказ в обслуживании», эта настройка и значение по умолчанию
были перенесены в Go 1.25.2 и Go 1.24.8.
Go 1.25
Go 1.25 добавила новую настройку decoratemappings, которая контролирует, будут ли анонимные
отображения памяти операционной системы аннотироваться контекстом их назначения.
Эти аннотации отображаются в /proc/self/maps и /proc/self/smaps как
“[anon: Go: …]”. Эта настройка используется только в Linux. Для Go 1.25 она по умолчанию
устанавливается в decoratemappings=1, что включает аннотации. Использование decoratemappings=0
возвращает поведение к версии до Go 1.25. Эта настройка фиксируется во время запуска программы
и не может быть изменена изменением переменной окружения GODEBUG после запуска программы.
Go 1.25 добавила новую настройку embedfollowsymlinks, которая контролирует, будет ли команда Go
следовать символическим ссылкам при встраивании файлов.
Значение по умолчанию embedfollowsymlinks=0 не позволяет следовать символическим ссылкам.
embedfollowsymlinks=1 разрешает следование символическим ссылкам.
Go 1.25 добавила новую настройку containermaxprocs, которая контролирует, будет ли среда выполнения Go
учитывать ограничения CPU в cgroup при установке значения GOMAXPROCS по умолчанию.
Значение по умолчанию containermaxprocs=1 будет использовать ограничения cgroup в дополнение к
общему количеству логических CPU и affinity CPU. containermaxprocs=0 отключает учёт ограничений cgroup.
Эта настройка влияет только на Linux.
Go 1.25 добавила новую настройку updatemaxprocs, которая контролирует, будет ли среда выполнения Go
периодически обновлять GOMAXPROCS при изменении CPU affinity или ограничений cgroup.
Значение по умолчанию updatemaxprocs=1 включает периодические обновления.
updatemaxprocs=0 отключает периодические обновления.
Go 1.25 отключила алгоритмы подписей SHA-1 в TLS 1.2 в соответствии с RFC 9155.
По умолчанию можно вернуть обратно с помощью настройки tlssha1=1.
Go 1.25 перешла на SHA-256 для заполнения отсутствующего SubjectKeyId в
crypto/x509.CreateCertificate. Настройка x509sha256skid=0 возвращает использование SHA-1.
Go 1.25 исправила семантику отчетов о конфликте для внутренних блокировок среды выполнения и, таким образом, удалила настройку runtimecontentionstacks.
Go 1.25 (начиная с Go 1.25 RC 2) отключила маркировку информации о сборке при обнаружении нескольких систем управления версиями (VCS) из-за опасений, связанных с атаками внедрения VCS. Это поведение и настройка были перенесены обратно в Go 1.24.5 и Go 1.23.11. Это поведение можно снова включить с помощью настройки allowmultiplevcs=1.
Go 1.24
Go 1.24 добавила новую настройку fips140, которая управляет тем, работает ли модуль криптографии Go в режиме FIPS 140-3.
Возможные значения:
- “off”: без специальной поддержки режима FIPS 140-3. Это значение по умолчанию.
- “on”: модуль криптографии Go работает в режиме FIPS 140-3.
- “only”: как “on”, но криптографические алгоритмы, не одобренные FIPS 140-3, возвращают ошибку или вызывают панику.
Для получения дополнительной информации см. Соответствие FIPS 140-3.
Эта настройка фиксируется во время запуска программы и не может быть изменена путем изменения переменной окружения
GODEBUGпосле запуска программы.
Go 1.24 изменила глобальную функцию math/rand.Seed на no-op. Это поведение контролируется настройкой randseednop.
Для Go 1.24 по умолчанию она равна randseednop=1.
Использование randseednop=0 восстанавливает поведение до Go 1.24.
Go 1.24 добавила новые значения для настройки multipathtcp.
Возможные значения для multipathtcp теперь следующие:
- “0”: по умолчанию отключает MPTCP для dialers и listeners
- “1”: по умолчанию включает MPTCP для dialers и listeners
- “2”: по умолчанию включает MPTCP только для listeners
- “3”: по умолчанию включает MPTCP только для dialers
Для Go 1.24 значение по умолчанию теперь равно multipathtcp=“2”, то есть MPTCP включён по умолчанию для listeners. Использование multipathtcp=“0” восстанавливает поведение до Go 1.24.
Go 1.24 изменила поведение go test -json таким образом, что ошибки сборки теперь выводятся в формате JSON вместо текста.
Эти новые события JSON отличаются новыми значениями Action, но могут вызвать проблемы с CI-системами, которые не устойчивы к таким событиям.
Это поведение можно контролировать с помощью настройки gotestjsonbuildtext.
Использование gotestjsonbuildtext=1 восстанавливает поведение Go 1.23.
Эта настройка будет удалена в будущем выпуске, не позднее Go 1.28.
Go 1.24 изменила поведение пакета crypto/rsa, требуя, чтобы ключи RSA были не менее 1024 бит.
Это поведение можно контролировать с помощью настройки rsa1024min.
Использование rsa1024min=0 восстанавливает поведение Go 1.23.
Go 1.24 ввела механизм включения специфичных для платформы режимов Data Independent Timing (DIT) в пакете crypto/subtle.
Этот режим может быть включен для всей программы с помощью настройки dataindependenttiming.
Для Go 1.24 по умолчанию она равна dataindependenttiming=0. При отсутствии настройки dataindependenttiming поведение по умолчанию остаётся таким же, как в Go 1.23.
Использование dataindependenttiming=1 включает режим DIT для всей программы на Go.
Когда он включён, DIT будет включён при вызове C-кода из Go. Когда включён, вызов Go-кода из C будет включать DIT и отключать его перед возвратом в C, если он не был включён при входе в Go-код.
На данный момент это влияет только на arm64 программы. Для всех остальных платформ это не имеет эффекта.
Go 1.24 удалила настройку x509sha1. Пакет crypto/x509 больше не поддерживает проверку подписей сертификатов, использующих алгоритмы подписи на основе SHA-1.
Go 1.24 изменяет значение по умолчанию для настройки x509usepolicies с 0 на 1. При маршалинге сертификатов политики теперь берутся из поля Certificate.Policies вместо поля Certificate.PolicyIdentifiers по умолчанию.
Go 1.24 включила механизм обмена ключами post-quantum X25519MLKEM768 по умолчанию. Отменить это можно с помощью настройки tlsmlkem. Это может быть полезно при работе с некорректно работающими TLS-серверами, которые не обрабатывают большие записи правильно, вызывая таймаут во время рукопожатия (см. TLS post-quantum TL;DR fail). Go 1.24 также удалила X25519Kyber768Draft00 и настройку tlskyber из Go 1.23.
Go 1.24 заставила ParsePKCS1PrivateKey использовать и проверять параметры CRT в закодированном закрытом ключе. Это поведение можно контролировать с помощью настройки x509rsacrt. Использование x509rsacrt=0 восстанавливает поведение Go 1.23.
Go 1.23
Go 1.23 изменила поведение каналов, создаваемых пакетом time, делая их не буферизованными (синхронными), что значительно упрощает корректное использование методов Timer.Stop и Timer.Reset. Настройка asynctimerchan отключает это изменение. Для данного изменения не предусмотрены метрики среды выполнения. Эта настройка может быть удалена в будущем релизе — как минимум в Go 1.27.
Go 1.23 изменила биты режима, возвращаемые os.Lstat и os.Stat для точек повторного анализа, что можно контролировать с помощью настройки winsymlink. Начиная с Go 1.23 (winsymlink=1), точки монтирования больше не имеют установленного флага os.ModeSymlink, а точки повторного анализа, которые не являются символическими ссылками, сокетами Unix или файлами дедупликации, теперь всегда имеют установленный флаг os.ModeIrregular. В результате этих изменений filepath.EvalSymlinks больше не разворачивает точки монтирования, что было источником множества несогласованностей и ошибок. В предыдущих версиях (winsymlink=0) точки монтирования рассматривались как символические ссылки, а другие точки повторного анализа с нестандартными битами типа os.ModeType (например, os.ModeDir) не имели установленного флага ModeIrregular.
В Go 1.23 изменена функция os.Readlink и filepath.EvalSymlinks
для того, чтобы избежать попыток нормализовать тома в буквы дисков, что не всегда даже возможно.
Поведение регулируется параметром winreadlinkvolume.
В Go 1.23 по умолчанию он равен winreadlinkvolume=1.
В предыдущих версиях по умолчанию он равен winreadlinkvolume=0.
В Go 1.23 по умолчанию включена экспериментальная механизм обмена ключами post-quantum
X25519Kyber768Draft00. Возврат к предыдущему поведению возможен с помощью
tlskyber настройки.
Это может быть полезно при работе с некорректно работающими TLS-серверами, которые не обрабатывают большие записи правильно,
что приводит к таймауту во время рукопожатия (см. TLS post-quantum TL;DR fail).
В Go 1.23 изменено поведение функции
crypto/x509.ParseCertificate для того,
чтобы отклонять отрицательные номера сертификатов. Это изменение можно отменить с помощью
x509negativeserial настройки.
В Go 1.23 по умолчанию вновь включена поддержка ECMAScript 6 шаблонных литералов в html/template.
Параметр jstmpllitinterp больше не влияет.
В Go 1.23 изменено поведение наборов шифров TLS по умолчанию, используемых клиентами и серверами,
если они не были явно настроены, убраны 3DES шифры. Возврат к предыдущему поведению возможен
с помощью tls3des настройки.
В Go 1.23 изменено поведение функций tls.X509KeyPair
и tls.LoadX509KeyPair для заполнения поля Leaf возвращаемого
tls.Certificate.
Поведение регулируется параметром x509keypairleaf. В Go 1.23 по умолчанию он равен
x509keypairleaf=1. В предыдущих версиях по умолчанию он равен
x509keypairleaf=0.
В Go 1.23 изменены функции
net/http.ServeContent,
net/http.ServeFile и
net/http.ServeFS для удаления заголовков Cache-Control,
Content-Encoding, Etag и Last-Modified при возврате ошибки. Поведение регулируется
параметром httpservecontentkeepheaders.
Использование httpservecontentkeepheaders=1 восстанавливает поведение до Go 1.23.
Go 1.22
В Go 1.22 добавлен настраиваемый лимит для контроля максимального допустимого размера ключа RSA,
который может использоваться в TLS-рукопожатиях, контролируется параметром tlsmaxrsasize.
По умолчанию он равен tlsmaxrsasize=8192, ограничивая RSA до 8192-битных ключей. Для защиты от атак типа denial of service
эта настройка и значение по умолчанию были перенесены в Go 1.19.13, Go 1.20.8 и Go 1.21.1.
В Go 1.22 было сделано так, что чтение запроса или ответа, выполненного с помощью клиента или сервера net/http, с пустым заголовком Content-Length теперь приводит к ошибке.
Это поведение регулируется параметром httplaxcontentlength.
В Go 1.22 было изменено поведение ServeMux для принятия расширенных шаблонов и разворачивания как шаблонов, так и путей запросов по сегментам.
Это поведение можно контролировать с помощью httpmuxgo121 настройки.
В Go 1.22 был добавлен тип Alias в пакет go/types
для явного представления псевдонимов типов.
То, будет ли тип чекер генерировать типы Alias, регулируется параметром
gotypesalias.
В Go 1.22 по умолчанию установлено значение gotypesalias=0.
В Go 1.23 значение gotypesalias=1 станет значением по умолчанию.
Этот параметр будет удалён в будущем релизе — как минимум в Go 1.27.
В Go 1.22 значение по умолчанию минимальной версии TLS, поддерживаемой как серверами, так и клиентами, было изменено на TLS 1.2.
По умолчанию можно вернуть TLS 1.0 с помощью параметра tls10server.
В Go 1.22 было изменено набор шифров TLS, используемых клиентами и серверами по умолчанию, если явно не указано иное.
Убраны шифры, использующие обмен ключами на основе RSA.
Вернуть предыдущее поведение можно с помощью параметра tlsrsakex.
В Go 1.22 был отключён
ConnectionState.ExportKeyingMaterial
в случае, если соединение не поддерживает ни TLS 1.3, ни Extended Master Secret
(реализовано в Go 1.21). Восстановить его можно с помощью параметра tlsunsafeekm.
В Go 1.22 было изменено взаимодействие среды выполнения с прозрачными большими страницами в Linux.
В частности, общая конфигурация ядра Linux может привести к значительным издержкам памяти, и Go 1.22 больше не обходит эту проблему по умолчанию.
Чтобы обойти эту проблему без изменения настроек ядра, прозрачные большие страницы можно отключить для памяти Go с помощью
disablethp настройки.
Это поведение было перенесено в Go 1.21.1, но параметр доступен только начиная с Go 1.21.6.
Этот параметр может быть удалён в будущем релизе, и пользователи, сталкивающиеся с этой проблемой,
должны настроить свою систему Linux согласно рекомендациям в GC guide, или перейти на дистрибутив Linux,
в котором прозрачные большие страницы отключены.
В Go 1.22 в mutex профиль были добавлены конфликты на внутренних блокировках среды выполнения.
Конфликты на этих блокировках всегда сообщаются как runtime._LostContendedRuntimeLock.
Полные стековые трассировки блокировок среды выполнения можно включить с помощью настройки runtimecontentionstacks.
Эти трассировки имеют нестандартную семантику, подробности см. в документации к параметру.
В Go 1.22 было добавлено новое поле crypto/x509.Certificate, Policies, которое поддерживает OID политик сертификатов с компонентами больше 31 бита. По умолчанию это поле используется только при разборе (parsing), когда оно заполняется OID политик, но не используется при маршалинге. Оно может быть использовано для маршалинга этих более длинных OID вместо существующего поля PolicyIdentifiers, с помощью x509usepolicies настройки.
Go 1.21
В Go 1.21 вызов panic с нулевым значением интерфейса стал ошибкой среды выполнения, контролируемой настройкой panicnil.
В Go 1.21 было сделано ошибочным использование действий html/template внутри литералов шаблонов ECMAScript 6, контролируемого настройкой
jstmpllitinterp.
Это поведение было перенесено в Go 1.19.8+ и Go 1.20.3+.
Go 1.21 ввёл ограничение на максимальное количество заголовков MIME и мультичастиотных форм, контролируемых настройками
multipartmaxheaders и multipartmaxparts
соответственно.
Это поведение было перенесено в Go 1.19.8+ и Go 1.20.3+.
Go 1.21 добавила поддержку Multipath TCP, но она используется только если приложение явно запросило её. Это поведение можно контролировать с помощью настройки multipathtcp.
Не планируется удалять какие-либо из этих настроек.
Go 1.20
Go 1.20 добавила поддержку отклонения небезопасных путей в архивах tar и zip, контролируемую настройками tarinsecurepath и zipinsecurepath.
По умолчанию они установлены в tarinsecurepath=1 и zipinsecurepath=1, сохраняя поведение предыдущих версий Go.
В будущих версиях Go значения по умолчанию могут измениться на tarinsecurepath=0 и zipinsecurepath=0.
Go 1.20 добавила автоматическое семплирование глобального генератора случайных чисел из math/rand, контролируемое настройкой randautoseed.
Go 1.20 ввела концепцию резервных корневых сертификатов, используемых во время проверки сертификатов, контролируемую настройкой x509usefallbackroots.
Go 1.20 удалила предустановленные файлы .a стандартной библиотеки из дистрибутива Go.
Установки теперь собирают и кэшируют стандартную библиотеку так же, как и другие пакеты модулей.
Настройка installgoroot восстанавливает установку и использование предустановленных файлов .a.
Не планируется удаление каких-либо из этих настроек.
Go 1.19
В Go 1.19 было сделано так, что разрешение путей, ведущих к исполняемым файлам в текущем каталоге, становится ошибкой, что контролируется настройкой execerrdot. Не планируется удаление этой настройки.
Go 1.19 начала отправлять дополнительные заголовки EDNS0 в DNS-запросах. Сообщается, что это может сломать DNS-сервер, предоставляемый некоторыми маршрутизаторами, такими как CenturyLink Zyxel C3000Z. Это можно изменить с помощью настройки netedns0. Эта настройка доступна в Go 1.21.12, Go 1.22.5, Go 1.23 и более поздних версиях. Не планируется удаление этой настройки.
Go 1.18
В Go 1.18 была удалена поддержка SHA1 в большинстве сертификатов X.509, что контролируется настройкой x509sha1. Эта настройка была удалена в Go 1.24.
Go 1.10
Go 1.10 изменил работу кэширования сборки и добавил кэширование тестов, а также настройки gocacheverify, gocachehash и gocachetest. Не планируется удаление этих настроек.
Go 1.6
Go 1.6 ввёл прозрачную поддержку HTTP/2, что контролируется настройками http2client, http2server и http2debug. Не планируется удаление этих настроек.
Go 1.5
Go 1.5 ввёл чисто Go DNS-резолвер, который контролируется настройкой netdns. Не планируется удаление этой настройки.