вторник, 29 августа 2023 г.

Релиз Go 1.21

Релиз Go, версия 1.21, выходит через шесть месяцев после Go 1.20. Большинство его изменений касаются реализации цепочки инструментов, среды выполнения и библиотек. Как всегда, выпуск поддерживает обещание совместимости Go 1; на самом деле Go 1.21 улучшает это обещание. Ожидается, что почти все программы Go продолжат компилироваться и работать, как и раньше.

В Go 1.21 внесено небольшое изменение в нумерацию релизов. В прошлом использовали Go 1.N для обозначения как общей языковой версии Go, так и семейства релизов, а также первого релиза в этом семействе. Начиная с версии Go 1.21, первый релиз теперь называется Go 1.N.0. В этот раз выпускается как язык Go 1.21, так и его первоначальная реализация, версию Go 1.21.0. Эти примечания относятся к "Go 1.21"; такие инструменты, как go version, будут сообщать "go1.21.0" (пока вы не обновитесь до версии Go 1.21.1).

Изменения в языке

Go 1.21 добавляет в язык три новых встроенных модуля.

  • Новые функции min и max вычисляют наименьшее (или наибольшее для max) значение фиксированного числа заданных аргументов.
  • Новая функция clear удаляет все элементы с карты или обнуляет все элементы среза.

Порядок инициализации пакетов теперь указан более точно. Новый алгоритм таков:

  • Отсортируйте все пакеты по пути импорта.
  • Повторяйте, пока список пакетов не станет пустым:
    • Найдите в списке первый пакет, для которого все импорты уже инициализированы.
    • Инициализируйте этот пакет и удалите его из списка.

Это может изменить поведение некоторых программ, которые полагаются на определенный порядок инициализации, не выраженный явным импортом. Поведение таких программ не было четко определено спецификацией в прошлых релизах. Новое правило дает однозначное определение.

Внесено множество улучшений, повышающих мощность и точность вывода типов.

  • Функция (возможно, частично созданная универсальная) теперь может быть вызвана с аргументами, которые сами являются (возможно, частично созданными) универсальными функциями. Компилятор попытается вывести отсутствующие аргументы типа вызываемого объекта (как и раньше) и для каждого аргумента, который является универсальной функцией, экземпляр которой не полностью создан, его отсутствующие аргументы типа (новые). Типичными вариантами использования являются вызовы универсальных функций, работающих с контейнерами (например, slices.IndexFunc), где аргумент функции также может быть универсальным, а аргумент типа вызываемой функции и ее аргументы выводятся из типа контейнера. В более общем смысле универсальную функцию теперь можно использовать без явного создания экземпляра, когда она присваивается переменной или возвращается как значение результата, если аргументы типа можно вывести из присваивания.
  • Вывод типа теперь также учитывает методы, когда значение присваивается интерфейсу: аргументы типа для параметров типа, используемых в сигнатурах методов, могут быть выведены из соответствующих типов параметров соответствующих методов.
  • Точно так же, поскольку аргумент типа должен реализовывать все методы соответствующего ограничения, методы аргумента типа и ограничения совпадают, что может привести к выводу дополнительных аргументов типа.
  • Если несколько нетипизированных постоянных аргументов разных типов (таких как нетипизированное целое число и нетипизированная константа с плавающей запятой) передаются параметрам с одним и тем же (не указанным иным образом) типом параметра типа вместо ошибки, теперь вывод типа определяет тип, используя тот же подход, что и у оператора с нетипизированными постоянными операндами. Это изменение приводит типы, выведенные из нетипизированных константных аргументов, в соответствие с типами константных выражений.
  • Вывод типа теперь является точным при сопоставлении соответствующих типов в присваиваниях: типы компонентов (такие как элементы срезов или типы параметров в сигнатурах функций) должны быть идентичными (с учетом подходящих аргументов типа) для сопоставления, иначе вывод завершится ошибкой. Это изменение создает более точные сообщения об ошибках: если в прошлом вывод типа мог завершиться неправильно и привести к недопустимому присвоению, теперь компилятор сообщает об ошибке вывода, если два типа не могут совпасть.

В более общем плане описание вывода типов в спецификации языка было уточнено. Вместе все эти изменения делают вывод типов более мощным, а ошибки вывода менее неожиданными.

Go 1.21 включает в себя предварительную версию изменения языка, которое рассматривается для будущей версии Go: создание переменных цикла for для каждой итерации, а не для каждого цикла, чтобы избежать случайного совместного использования ошибок.

Go 1.21 теперь определяет, что если горутина находится в состоянии паники, а функция recovery была вызвана непосредственно отложенной функцией, возвращаемое значение recovery гарантированно не равно нулю. Чтобы гарантировать это, вызов panic со значением интерфейса nil (или нетипизированным nil) вызывает панику во время выполнения типа *runtime.PanicNilError.

Для поддержки программ, написанных для более старых версий Go, nil panic можно повторно включить, установив GODEBUG=panicnil=1. Этот параметр включается автоматически при компиляции программы, основной пакет которой находится в модуле, объявляющем go 1.20 или более ранней версии.

Инструменты

Go 1.21 добавляет улучшенную поддержку обратной и прямой совместимости в цепочке инструментов Go.

Чтобы улучшить обратную совместимость, Go 1.21 формализует использование Go переменной среды GODEBUG для управления поведением по умолчанию для изменений, которые не являются критическими в соответствии с политикой совместимости, но, тем не менее, могут привести к нарушению работы существующих программ. (Например, программы, зависящие от ошибочного поведения, могут сломаться, когда ошибка будет исправлена, но исправления ошибок не считаются критическими изменениями.) Когда Go должен внести такое изменение поведения, теперь он выбирает между старым и новым поведением на основе go в файле go.work рабочей области или в файле go.mod основного модуля. При обновлении до новой цепочки инструментов Go, но с сохранением исходной (старой) версии Go для строки go сохраняется поведение старой цепочки инструментов. Благодаря такой поддержке совместимости последняя цепочка инструментов Go всегда должна быть лучшей и наиболее безопасной реализацией старой версии Go.

Для улучшения совместимости Go 1.21 теперь считывает строку go в файле go.work или go.mod как строгое минимальное требование: go 1.21.0 означает, что рабочее пространство или модуль нельзя использовать с Go 1.20 или Go 1.21rc1. Это позволяет проектам, которые зависят от исправлений, сделанных в более поздних версиях Go, гарантировать, что они не используются с более ранними версиями. Это также дает более качественные отчеты об ошибках для проектов, использующих новые функции Go: когда проблема заключается в том, что требуется более новая версия Go, об этой проблеме сообщается четко, вместо того, чтобы пытаться построить код и печатать ошибки о неразрешенных импортах или синтаксических ошибках.

Чтобы упростить управление этими новыми более строгими требованиями к версии, команда go теперь может вызывать не только цепочку инструментов, входящую в ее собственный релиз, но и другие версии цепочки инструментов Go, найденные в PATH или загруженные по запросу. Если строка go.mod или go.work объявляет минимальные требования к более новой версии Go, команда go автоматически найдет и запустит эту версию. Новая директива цепочки инструментов устанавливает рекомендуемый минимальный набор инструментов для использования, который может быть новее, чем строгий минимум.

Команда go

Флаг сборки -pgo теперь по умолчанию имеет значение -pgo=auto, а ограничение на указание одного основного пакета в командной строке теперь снято. Если файл с именем default.pgo присутствует в каталоге основного пакета, команда go будет использовать его, чтобы включить оптимизацию на основе профиля для сборки соответствующей программы.

Флаг -C dir теперь должен быть первым флагом в командной строке при использовании.

Новая опция go test -fullpath печатает полные пути в сообщениях журнала тестирования, а не только базовые имена.

Флаг go test -c теперь поддерживает запись тестовых двоичных файлов для нескольких пакетов, каждый в pkg.test, где pkg — это имя пакета. Будет ошибкой, если более одного компилируемого тестового пакета имеют заданное имя пакета.

Флаг go test -o теперь принимает аргумент каталога, и в этом случае тестовые двоичные файлы записываются в этот каталог, а не в текущий каталог.

Cgo

В файлах, которые импортируют "C", набор инструментов Go теперь правильно сообщает об ошибках при попытках объявить методы Go для типов C.

Среда времени исполнения (Runtime)

При печати очень глубоких стеков runtime теперь печатает первые 50 (самые внутренние) фреймы, а затем нижние 50 (самые внешние) фреймы, а не просто печатает первые 100 фреймов. Это облегчает понимание того, как запускаются глубоко рекурсивные стеки, и особенно полезно для отладки переполнения стека.

На платформах Linux, которые поддерживают прозрачные огромные страницы (transparent huge pages), среда выполнения Go теперь более явно определяет, какие части кучи могут быть поддержаны огромными страницами. Это приводит к лучшему использованию памяти: для небольших куч должно использоваться меньше памяти (до 50 % в патологических случаях), тогда как для больших куч должно использоваться меньше испорченных огромных страниц для плотных частей кучи, что улучшает использование ЦП и задержку до 1%.

В результате внутренней настройки сборки мусора во время выполнения приложения могут сократить задержку хвоста приложения на 40% и небольшое снижение использования памяти. В некоторых приложениях также может наблюдаться небольшая потеря пропускной способности. Уменьшение использования памяти должно быть пропорционально потере пропускной способности, чтобы компромисс между пропускной способностью и памятью предыдущей версии можно было восстановить (с небольшим изменением задержки) путем небольшого увеличения GOGC и/или GOMEMLIMIT.

Вызовы из C в Go в потоках, созданных в C, требуют некоторой настройки для подготовки к выполнению Go. На платформах Unix эта настройка теперь сохраняется при нескольких вызовах из одного потока. Это значительно снижает накладные расходы на последующие вызовы C в Go с ~1–3 микросекунд на вызов до ~100–200 наносекунд на вызов.

Компилятор

Оптимизация на основе профиля (PGO, Profile-guide optimization), добавленная в качестве предварительной версии в Go 1.20, теперь готова для общего использования. PGO обеспечивает дополнительную оптимизацию кода, который определяется как "горячий" в соответствии с профилями производственных рабочих нагрузок. Как упоминалось в разделе команд Go, PGO включен по умолчанию для двоичных файлов, которые содержат профиль default.pgo в основном каталоге пакета. Улучшение производительности варьируется в зависимости от поведения приложения: в большинстве программ из репрезентативного набора программ Go наблюдается улучшение от 2 до 7% от включения PGO.

Сборки PGO теперь могут девиртуализировать вызовы некоторых методов интерфейса, добавляя конкретный вызов к наиболее распространенному вызываемому объекту. Это обеспечивает дальнейшую оптимизацию, например, встраивание вызываемого объекта.

Go 1.21 повышает скорость сборки до 6%, во многом благодаря сборке самого компилятора с помощью PGO.

Ассемблер

В amd64 безрамочные функции сборки nosplit больше не помечаются автоматически как NOFRAME. Вместо этого при желании атрибут NOFRAME должен быть явно указан, что уже происходит в других архитектурах, поддерживающих указатели фреймов. Благодаря этому среда выполнения теперь поддерживает указатели фреймов для переходов стека.

Улучшен верификатор, проверяющий неправильное использование R15 при динамической компоновке на amd64.

Компоновщик (Linker)

В windows/amd64 компоновщик (с помощью компилятора) теперь по умолчанию выдает данные размотки SEH, что улучшает интеграцию приложений Go с отладчиками Windows и другими инструментами.

В Go 1.21 компоновщик (с помощью компилятора) теперь способен удалять неработающие (не имеющие ссылок) глобальные переменные карты, если количество записей в инициализаторе переменных достаточно велико и если выражения инициализатора не имеют побочных эффектов.

Основная библиотека

Новый пакет log/slog

Новый пакет log/slog обеспечивает структурированное ведение журналов по уровням. Структурированное ведение журналов генерирует пары "ключ-значение", что обеспечивает быструю и точную обработку больших объемов данных журнала. Пакет поддерживает интеграцию с популярными инструментами и сервисами анализа журналов.

Новый пакет testing/slogtest

Новый пакет testing/slogtest может помочь проверить реализации slog.Handler.

Новый пакет slices

Новый пакет slices предоставляет множество общих операций над срезами, используя общие функции, которые работают со срезами любого типа элементов.

Новый пакет maps

Новый пакет maps предоставляет несколько общих операций с картами, используя общие функции, которые работают с картами любого типа ключа или элемента.

Новый пакет cmp

Новый пакет cmp определяет ограничение типа Ordered и две новые универсальные функции Less и Compare, полезные для упорядоченных типов.

Небольшие изменения в библиотеке

Как всегда, в библиотеку внесены различные незначительные изменения и обновления, сделанные с учетом обеспечения совместимости с Go 1. Существуют также различные улучшения производительности, которые здесь не перечислены.

archive/tar

Реализация интерфейса io/fs.FileInfo, возвращаемого Header.FileInfo, теперь реализует метод String, который вызывает io/fs.FormatFileInfo.

archive/zip

Реализация интерфейса io/fs.FileInfo, возвращаемого FileHeader.FileInfo, теперь реализует метод String, который вызывает io/fs.FormatFileInfo.

Реализация интерфейса io/fs.DirEntry, возвращаемого методом io/fs.ReadDirFile.ReadDir io/fs.File, возвращаемого Reader.Open, теперь реализует метод String, который вызывает io/fs.FormatDirEntry.

bytes

Тип Buffer имеет два новых метода: Available и AvailableBuffer. Их можно использовать вместе с методом Write для добавления непосредственно в буфер.

context

Новая функция WithoutCancel возвращает копию контекста, которая не отменяется при отмене исходного контекста.

Новые функции WithDeadlineCause и WithTimeoutCause позволяют установить причину отмены контекста по истечении крайнего срока или таймера. Причину можно выяснить с помощью функции Cause.

Новая функция AfterFunc регистрирует функцию, которая будет запускаться после отмены контекста.

Оптимизация означает, что результаты вызова Background и TODO и преобразования их в общий тип можно считать равными. В предыдущих релизах они всегда были разными. Сравнение значений контекста на предмет равенства никогда не было четко определено, поэтому это не считается несовместимым изменением.

crypto/ecdsa

PublicKey.Equal и PrivateKey.Equal теперь выполняются за постоянное время.

crypto/elliptic

Все методы Curve, а также методы GenerateKey, Marshal и Unmarshal устарели. Для операций ECDH вместо этого следует использовать новый пакет crypto/ecdh. Для операций более низкого уровня используйте сторонние модули, такие как filippo.io/nistec.

crypto/rand

Пакет crypto/rand теперь использует системный вызов getrandom в NetBSD 10.0 и более поздних версиях.

crypto/rsa

Производительность частных операций RSA (расшифровка и подпись) теперь лучше, чем в Go 1.19 для GOARCH=amd64 и GOARCH=arm64. Производительность снижалась в Go 1.20.

Из-за добавления частных полей в PrecomputedValues PrivateKey.Precompute необходимо вызывать для оптимальной производительности даже при десериализации (например, из JSON) предварительно вычисленного закрытого ключа.

PublicKey.Equal и PrivateKey.Equal теперь выполняются за постоянное время.

Функция GenerateMultiPrimeKey и поле PrecomputedValues.CRTValues устарели. PrecomputedValues.CRTValues по-прежнему будет заполняться при вызове PrivateKey.Precompute, но значения не будут использоваться во время операций расшифровки.

crypto/sha256

Операции SHA-224 и SHA-256 теперь используют собственные инструкции, если они доступны при GOARCH=amd64, что обеспечивает повышение производительности примерно в 3–4 раза.

crypto/tls

Серверы теперь пропускают проверку клиентских сертификатов (в том числе отсутствие запуска Config.VerifyPeerCertificate) для возобновленных соединений, помимо проверки срока действия. Это увеличивает размер билетов сеанса, когда используются клиентские сертификаты. Клиенты уже пропускали проверку при возобновлении, но теперь проверяйте время истечения срока действия, даже если установлен Config.InsecureSkipVerify.

Приложения теперь могут контролировать содержимое билетов сеанса.

  • Новый тип SessionState описывает возобновляемый сеанс.
  • Метод SessionState.Bytes и функция ParseSessionState сериализуют и десериализуют SessionState.
  • Перехватчики Config.WrapSession и Config.UnwrapSession преобразуют SessionState в билет и обратно на стороне сервера.
  • Методы Config.EncryptTicket и Config.DecryptTicket предоставляют реализацию WrapSession и UnwrapSession по умолчанию.
  • Метод ClientSessionState.ResumptionState и функция NewResumptionState могут использоваться реализацией ClientSessionCache для хранения и возобновления сеансов на стороне клиента.

Чтобы уменьшить вероятность использования билетов сеанса в качестве механизма отслеживания между соединениями, сервер теперь выдает новые билеты при каждом возобновлении (если они поддерживаются и не отключены), и билеты больше не содержат идентификатор ключа, который их зашифровал. Передача большого количества ключей в Conn.SetSessionTicketKeys может привести к заметному снижению производительности.

И клиенты, и серверы теперь реализуют расширение Extended Master Secret (RFC 7627). Устаревание ConnectionState.TLSUnique отменено и теперь установлено для возобновленных соединений, поддерживающих расширенный главный секрет.

Новый тип QUICConn обеспечивает поддержку реализаций QUIC, включая поддержку 0-RTT. Обратите внимание, что это само по себе не является реализацией QUIC, и 0-RTT по-прежнему не поддерживается в TLS.

Новая функция VersionName возвращает имя для номера версии TLS.

Были улучшены коды предупреждений TLS, отправляемые с сервера при ошибках аутентификации клиента. Раньше эти сбои всегда приводили к предупреждению о "плохом сертификате". Теперь при определенных сбоях будут использоваться более подходящие коды предупреждений, как определено в RFC 5246 и RFC 8446:

  • Для подключений TLS 1.3, если сервер настроен на требование проверки подлинности клиента с помощью RequireAnyClientCert или RequireAndVerifyClientCert, а клиент не предоставляет сертификат, сервер теперь вернет предупреждение "требуется сертификат".
  • Если клиент предоставляет сертификат, который не подписан набором доверенных центров сертификации, настроенных на сервере, сервер вернет предупреждение "неизвестный центр сертификации".
  • Если клиент предоставляет сертификат, срок действия которого истек или еще не действителен, сервер вернет предупреждение "сертификат с истекшим сроком действия".
  • Во всех других сценариях, связанных с ошибками аутентификации клиента, сервер по-прежнему возвращает "неверный сертификат".

crypto/x509

RevokedList.RevokedCertificates устарел и заменен новым полем RevokedCertificateEntries, которое является частью RevokedListEntry. RelocationListEntry содержит все поля в pkix.RevokedCertificate, а также код причины отзыва.

Ограничения имен теперь правильно применяются к нелистовым сертификатам, а не к сертификатам, в которых они выражены.

debug/elf

Новый метод File.DynValue можно использовать для получения числовых значений, перечисленных в данном динамическом теге.

Постоянные флаги, разрешенные в динамическом теге DT_FLAGS_1, теперь определяются с типом DynFlag1. Эти теги имеют имена, начинающиеся с DF_1.

Пакет теперь определяет константу COMPRESS_ZSTD.

Пакет теперь определяет константу R_PPC64_REL24_P9NOTOC.

debug/pe

Попытки чтения из раздела, содержащего неинициализированные данные, с помощью Section.Data или средства чтения, возвращаемого Section.Open, теперь возвращают ошибку.

embed

io/fs.File, возвращаемый FS.Open, теперь имеет метод ReadAt, реализующий io.ReaderAt.

Вызов FS.Open.Stat вернет тип, который теперь реализует метод String, вызывающий io/fs.FormatFileInfo.

errors

Новая ошибка ErrUnsupported предоставляет стандартизированный способ указать, что запрошенная операция не может быть выполнена, поскольку она не поддерживается. Например, вызов os.Link при использовании файловой системы, не поддерживающей жесткие ссылки.

flag

Новая функция BoolFunc и метод FlagSet.BoolFunc определяют флаг, который не требует аргумента, и вызывает функцию при использовании флага. Это похоже на Func, но с логическим флагом.

Определение флага (через Bool, BoolVar, Int, IntVar и т. д.) вызовет панику, если Set уже был вызван для флага с таким же именем. Это изменение предназначено для обнаружения случаев, когда изменения в порядке инициализации приводят к тому, что операции с флагами выполняются в другом порядке, чем ожидалось. Во многих случаях решение этой проблемы состоит в том, чтобы ввести явную зависимость пакета, чтобы правильно упорядочить определение перед любыми операциями Set.

go/ast

Новый предикат IsGenerated сообщает, содержит ли синтаксическое дерево файла специальный комментарий, который обычно указывает на то, что файл был создан с помощью инструмента.

Новое поле File.GoVersion записывает минимальную версию Go, необходимую для любых директив //go:build или // +build.

go/build

Пакет теперь анализирует директивы сборки (комментарии, начинающиеся с //go:) в заголовках файлов (перед объявлением пакета). Эти директивы доступны в новых полях пакета: Directives, TestDirectives и XTestDirectives.

go/build/constraint

Новая функция GoVersion возвращает минимальную версию Go, подразумеваемую выражением сборки.

go/token

Новый метод File.Lines возвращает таблицу номеров строк файла в той же форме, которая принята File.SetLines.

go/types

Новый метод Package.GoVersion возвращает версию языка Go, использованную для проверки пакета.

hash/maphash

Пакет hash/maphash теперь имеет реализацию на чистом Go, которую можно выбрать с помощью тега сборки purego.

html/template

Новая ошибка ErrJSTemplate возвращается, когда действие появляется в литерале шаблона JavaScript. Ранее возвращалась неэкспортированная ошибка.

io/fs

Новая функция FormatFileInfo возвращает отформатированную версию FileInfo. Новая функция FormatDirEntry возвращает отформатированную версию DirEntry. Реализация DirEntry, возвращаемая ReadDir, теперь реализует метод String, который вызывает FormatDirEntry, и то же самое справедливо для значения DirEntry, переданного в WalkDirFunc.

math/big

Новый метод Int.Float64 возвращает ближайшее целое число с плавающей запятой вместе с указанием любого произошедшего округления.

net

В Linux пакет net теперь может использовать Multipath TCP, если ядро его поддерживает. По умолчанию он не используется. Чтобы использовать Multipath TCP, если он доступен на клиенте, вызовите метод Dialer.SetMultipathTCP перед вызовом методов Dialer.Dial или Dialer.DialContext. Чтобы использовать Multipath TCP, если он доступен на сервере, вызовите метод ListenConfig.SetMultipathTCP перед вызовом метода ListenConfig.Listen. Укажите сеть как "tcp", "tcp4" или "tcp6", как обычно. Если Multipath TCP не поддерживается ядром или удаленным хостом, соединение автоматически переключается на TCP. Чтобы проверить, использует ли конкретное соединение Multipath TCP, используйте метод TCPConn.MultipathTCP.

В будущем релизе Go возможно будет включен Multipath TCP по умолчанию в системах, которые его поддерживают.

net/http

Новый метод ResponseController.EnableFullDuplex позволяет обработчикам сервера одновременно читать тело запроса HTTP/1 во время записи ответа. Обычно сервер HTTP/1 автоматически потребляет оставшееся тело запроса перед началом записи ответа, чтобы избежать взаимоблокировки клиентов, которые пытаются записать полный запрос до чтения ответа. Метод EnableFullDuplex отключает такое поведение.

Новая ошибка ErrSchemeMismatch возвращается клиентом и транспортом, когда сервер отвечает на запрос HTTPS ответом HTTP.

Пакет net/http теперь поддерживает error.ErrUnsupported, поскольку выражение error.Is(http.ErrNotSupported, error.ErrUnsupported) вернет true.

os

Программы теперь могут передавать пустое значение time.Time функции Chtimes, чтобы оставить неизменным либо время доступа, либо время модификации.

В Windows метод File.Chdir теперь меняет текущий каталог на файл, а не всегда возвращает ошибку.

В системах Unix, если в NewFile передается неблокирующий дескриптор, вызов метода File.Fd теперь будет возвращать неблокирующий дескриптор. Ранее дескриптор был переведен в режим блокировки.

В Windows вызов Truncate для несуществующего файла используется для создания пустого файла. Теперь он возвращает ошибку, указывающую, что файл не существует.

В Windows при вызове TempDir теперь используется GetTempPath2W, если он доступен, вместо GetTempPathW. Новое поведение представляет собой меру усиления безопасности, которая предотвращает доступ к временным файлам, созданным процессами, работающими как SYSTEM, со стороны не-SYSTEM процессов.

В Windows пакет os теперь поддерживает работу с файлами, имена которых, сохраненные в формате UTF-16, не могут быть представлены как действительные UTF-8.

В Windows Lstat теперь разрешает символические ссылки для путей, заканчивающихся разделителем путей, в соответствии с его поведением на платформах POSIX.

Реализация интерфейса io/fs.DirEntry, возвращаемого функцией ReadDir и методом File.ReadDir, теперь реализует метод String, который вызывает io/fs.FormatDirEntry.

Реализация интерфейса io/fs.FS, возвращаемого функцией DirFS, теперь реализует интерфейсы io/fs.ReadFileFS и io/fs.ReadDirFS.

path/filepath

Реализация интерфейса io/fs.DirEntry, передаваемого в аргумент функции WalkDir, теперь реализует метод String, который вызывает io/fs.FormatDirEntry.

reflect

В Go 1.21 ValueOf больше не принудительно размещает свой аргумент в куче, позволяя размещать содержимое Value в стеке. Большинство операций с Value также позволяют размещать базовое значение в стеке.

Новый метод Value Value.Clear очищает содержимое карты или обнуляет содержимое среза. Это соответствует новой встроенной функции clear, добавленной в язык.

Типы SliceHeader и StringHeader устарели. В новом коде отдавайте предпочтение unsafe.Slice, unsafe.SliceData, unsafe.String или unsafe.StringData.

regexp

Regexp теперь определяет методы MarshalText и UnmarshalText. Они реализуют encoding.TextMarshaler и encoding.TextUnmarshaler и будут использоваться такими пакетами, как encoding/json.

runtime

Текстовые трассировки стека, создаваемые программами Go, например, при сбое, вызове runtime.Stack или сборе профиля горутины с debug=2, теперь включают в трассировку стека идентификаторы горутин, создавших каждую горутину.

Сбойные приложения Go теперь могут подключиться к отчетам об ошибках Windows (WER, Windows Error Reporting), установив переменную среды GOTRACEBACK=wer или вызвав debug.SetTraceback("wer") перед сбоем. За исключением включения WER, среда выполнения будет вести себя так же, как и при GOTRACEBACK=crash. В системах, отличных от Windows, GOTRACEBACK=wer игнорируется.

GODEBUG=cgocheck=2, тщательная проверка правил передачи указателей cgo, больше не доступен в качестве опции отладки. Вместо этого он доступен в качестве эксперимента с использованием GOEXPERIMENT=cgocheck2. В частности, это означает, что этот режим необходимо выбирать во время сборки, а не во время запуска.

GODEBUG=cgocheck=1 по-прежнему доступен (и по-прежнему используется по умолчанию).

В пакет среды выполнения добавлен новый тип Pinner. Пиннеры могут использоваться для "закрепления" памяти Go, чтобы ее можно было более свободно использовать кодом, отличным от Go. Например, теперь разрешена передача значений Go, которые ссылаются на закрепленную память Go, в код C. Ранее передача любой такой вложенной ссылки была запрещена правилами передачи указателей cgo.

runtime/metrics

Теперь доступны некоторые ранее внутренние показатели сборщика мусора, такие как размер динамической кучи. GOGC и GOMEMLIMIT теперь также доступны в качестве метрик.

runtime/trace

Сбор трассировок на amd64 и arm64 теперь требует существенно меньших затрат ЦП: до 10 раз больше, чем в предыдущей версии.

Трассировки теперь содержат явные события stop-the-world по любой причине, по которой среда выполнения Go может запустить stop-the-world, а не только для сборки мусора.

sync

Новые функции OnceFunc, OnceValue и OnceValues отражают распространенное использование Once для ленивой инициализации значения при первом использовании.

syscall

В Windows функция Fchdir теперь меняет текущий каталог на свой аргумент, а не всегда возвращает ошибку.

Во FreeBSD SysProcAttr имеет новое поле Jail, которое можно использовать для помещения вновь созданного процесса в изолированную среду.

В Windows пакет syscall теперь поддерживает работу с файлами, имена которых, сохраненные в формате UTF-16, не могут быть представлены как допустимые UTF-8. Функции UTF16ToString и UTF16FromString теперь преобразуют данные UTF-16 в строки WTF-8. Это обратно совместимо, поскольку WTF-8 является расширенным набором формата UTF-8, который использовался в более ранних релизах.

Несколько значений ошибок соответствуют новым error.ErrUnsupported, например, error.Is(err, error.ErrUnsupported) возвращает true.

  • ENOSYS
  • ENOTSUP
  • EOPNOTSUPP
  • EPLAN9 (только Plan 9)
  • ERROR_CALL_NOT_IMPLEMENTED (только Windows)
  • ERROR_NOT_SUPPORTED (только Windows)
  • EWINDOWS (только Windows)

testing

Новая опция -test.fullpath будет печатать полные пути в сообщениях журнала тестирования, а не только базовые имена.

Новая функция тестирования сообщает, является ли программа тестом, созданным go test.

testing/fstest

Вызов Open.Stat вернет тип, который теперь реализует метод String, вызывающий io/fs.FormatFileInfo.

unicode

Пакет unicode и связанная с ним поддержка во всей системе были обновлены до Unicode 15.0.0.

Порты

Darwin

Как объявлено в примечаниях к релизу Go 1.20, для Go 1.21 требуется macOS 10.15 Catalina или более поздняя версия; поддержка предыдущих версий прекращена.

Windows

Как объявлено в примечаниях к релизу Go 1.20, для Go 1.21 требуется как минимум Windows 10 или Windows Server 2016; поддержка предыдущих версий прекращена.

WebAssembly

Новую директиву go:wasmimport теперь можно использовать в программах Go для импорта функций с хоста WebAssembly.

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

Системный интерфейс WebAssembly

В Go 1.21 добавлен экспериментальный порт в системный интерфейс WebAssembly (WASI), предварительная версия 1 (GOOS=wasip1, GOARCH=wasm).

В результате добавления нового значения GOOS "wasip1" файлы Go с именем *_wasip1.go теперь будут игнорироваться инструментами Go, за исключением случаев, когда используется это значение GOOS. Если у вас есть имена файлов, соответствующие этому шаблону, вам нужно будет переименовать их.

ppc64/ppc64le

В Linux GOPPC64=power10 теперь генерирует инструкции для PC, инструкции с префиксами и другие новые инструкции Power10. В AIX GOPPC64=power10 генерирует инструкции Power10, но не создает инструкции, относящиеся к PC.

При построении позиционно-независимых двоичных файлов для GOPPC64=power10 GOOS=linux GOARCH=ppc64le пользователи могут ожидать уменьшения размера двоичных файлов в большинстве случаев, в некоторых случаях на 3,5%. Независимые от позиции двоичные файлы создаются для ppc64le со следующими значениями -buildmode: c-archive, c-shared, shared, pie, plugin.

loong64

Порт linux/loong64 теперь поддерживает -buildmode=c-archive, -buildmode=c-shared и -buildmode=pie.


Читайте также:


вторник, 21 февраля 2023 г.

Релиз Go 1.20

Релиз Go, версия 1.20, выходит через шесть месяцев после Go 1.19. Большинство его изменений касаются реализации цепочки инструментов, среды выполнения и библиотек. Как всегда, релиз поддерживает обещание совместимости Go 1. Ожидается, что почти все программы Go продолжат компилироваться и работать, как и раньше.

Изменения в языке

Go 1.20 включает четыре изменения языка.

В Go 1.17 добавлены преобразования из среза в указатель массива. Go 1.20 расширяет это, чтобы разрешить преобразования из среза в массив: для среза x теперь можно записать [4]byte(x) вместо *(*[4]byte)(x).

Пакет unsafe определяет три новые функции SliceData, String и StringData. Наряду с Slice из Go 1.17 эти функции теперь предоставляют полную возможность конструировать и деконструировать значения срезов и строк, не завися от их точного представления.

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

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

Порты


Windows

Go 1.20 — это последний релиз, который будет работать в любой версии Windows 7, 8, Server 2008 и Server 2012. Для Go 1.21 потребуется как минимум Windows 10 или Server 2016.

Darwin и iOS

Go 1.20 — это последний релиз, который будет работать на macOS 10.13 High Sierra или 10.14 Mojave. Go 1.21 потребует macOS 10.15 Catalina или более поздней версии.

FreeBSD/RISC-V

Go 1.20 добавляет экспериментальную поддержку FreeBSD на RISC-V (GOOS=freebsd, GOARCH=riscv64).

Инструменты


Команда go

Каталог $GOROOT/pkg больше не хранит предварительно скомпилированные архивы пакетов для стандартной библиотеки: go install больше не записывает их, сборка go больше не проверяет их, и дистрибутив Go больше не отправляет их. Вместо этого пакеты в стандартной библиотеке собираются по мере необходимости и кэшируются в кэше сборки, как и пакеты вне GOROOT. Это изменение уменьшает размер дистрибутива Go, а также позволяет избежать перекоса инструментальной цепочки C для пакетов, использующих cgo.

Реализация go test -json была улучшена, чтобы сделать ее более надежной. Программы, запускающие go test -json, не нуждаются в каких-либо обновлениях. Программы, которые напрямую вызывают инструмент go test2json, теперь должны запускать двоичный файл теста с параметром -v=test2json (например, go test -v=test2json или ./pkg.test -test.v=test2json) вместо простого -v.

Связанное с этим изменение для go test -json — это добавление события с набором действий для запуска в начале выполнения каждой тестовой программы. При запуске нескольких тестов с помощью команды go эти стартовые события гарантированно будут генерироваться в том же порядке, что и пакеты, указанные в командной строке.

Команда go теперь определяет теги построения функций архитектуры, такие как amd64.v2, чтобы позволить выбрать файл реализации пакета на основе наличия или отсутствия конкретной функции архитектуры.

Подкоманды go теперь принимают -C <dir> для изменения каталога на <dir> перед выполнением команды, что может быть полезно для сценариев, которым необходимо выполнять команды в нескольких разных модулях.

Команды go build и go test больше не принимают флаг -i, который устарел, начиная с Go 1.16.

Команда go generate теперь принимает -skip <pattern> для пропуска директив //go:generate, соответствующих <pattern>.

Команда go test теперь принимает параметр -skip <pattern>, чтобы пропустить тесты, подтесты или примеры, соответствующие <pattern>.

Когда основной модуль находится в GOPATH/src, go install больше не устанавливает библиотеки для неосновных пакетов в GOPATH/pkg, а go list больше не сообщает поле Target для таких пакетов. (В модульном режиме скомпилированные пакеты хранятся только в кэше сборки, но из-за ошибки цели установки GOPATH неожиданно остались в силе.)

Команды go build, go install и другие, связанные со сборкой, теперь поддерживают флаг -pgo, который включает оптимизацию на основе профиля. Флаг -pgo указывает путь к файлу профиля. Указание -pgo=auto заставляет команду go искать файл с именем default.pgo в каталоге пакета main и использовать его, если он есть. В настоящее время для этого режима требуется, чтобы в командной строке был указан один main пакет, но планируется снять это ограничение в будущем релизе. Указание -pgo=off отключает оптимизацию на основе профиля.

Команды go build, go install и другие команды, связанные со сборкой, теперь поддерживают флаг -cover, который выполняет сборку указанной цели с инструментированием покрытия кода.

go version

Команда go version -m теперь поддерживает чтение дополнительных типов двоичных файлов Go, в первую очередь библиотек DLL Windows, созданных с помощью go build -buildmode=c-shared, и двоичных файлов Linux без разрешения на выполнение.

Cgo

Команда go теперь отключает cgo по умолчанию в системах без цепочки инструментов C. В частности, когда переменная среды CGO_ENABLED не установлена, переменная среды CC не установлена, а компилятор C по умолчанию (обычно clang или gcc) не найден в пути, CGO_ENABLED по умолчанию имеет значение 0. Как всегда, вы можете переопределить значение по умолчанию, установка CGO_ENABLED явно.

Наиболее важным эффектом изменения по умолчанию является то, что когда Go устанавливается в системе без компилятора C, теперь он будет использовать чистые сборки Go для пакетов в стандартной библиотеке, которые используют cgo, вместо использования предустановленных архивов пакетов (которые были удалены, как указано выше) или попытки использовать cgo и неудаче при этом. Это позволяет Go лучше работать в некоторых средах с минимальным контейнером, а также в macOS, где предустановленные архивы пакетов не использовались для пакетов на основе cgo, начиная с версии Go 1.16.

Пакеты в стандартной библиотеке, использующие cgo, это net, os/user и plugin. В macOS пакеты net и os/user были переписаны, чтобы не использовать cgo: теперь один и тот же код используется для сборок cgo и не-cgo, а также кросс-компилируемых сборок. В Windows пакеты net и os/user никогда не использовали cgo. В других системах сборки с отключенным cgo будут использовать чистую версию Go этих пакетов.

В macOS детектор гонок был переписан, чтобы не использовать cgo: программы с включенным детектором гонок можно создавать и запускать без Xcode. В Linux и других системах Unix, а также в Windows для использования детектора гонки требуется цепочка инструментов C на хосте.

Cover (Покрытие)

Go 1.20 поддерживает сбор профилей покрытия кода для программ (приложений и интеграционных тестов), а не только модульных тестов.

Чтобы собрать данные о покрытии для программы, соберите ее с флагом -cover команды go build, затем запустите полученный двоичный файл с переменной среды GOCOVERDIR, установленной в выходной каталог для профилей покрытия.

Ветеринар (vet)

Улучшено обнаружение захвата переменных цикла вложенными функциями.

Инструмент vet теперь сообщает о ссылках на переменные цикла после вызова T.Parallel() в телах функций подтеста. В таких ссылках может наблюдаться значение переменной из другой итерации (что обычно приводит к пропуску тестовых случаев) или недопустимое состояние из-за несинхронизированного конкурентного доступа.

Инструмент также обнаруживает ошибки ссылок в большем количестве мест. Раньше он рассматривал только последний оператор тела цикла, но теперь он рекурсивно проверяет последние операторы в операторах if, switch и select.

Новая диагностика для некорректных форматов времени

Инструмент vet теперь сообщает об использовании формата времени 2006-02-01 (гггг-дд-мм) с Time.Format и time.Parse. Этот формат не встречается в общих стандартах даты, но часто используется по ошибке при попытке использовать формат даты ISO 8601 (гггг-мм-дд).

Runtime

Некоторые из внутренних структур данных сборщика мусора были реорганизованы, чтобы освободить больше места и CPU. Это изменение снижает нагрузку на память и повышает общую производительность процессора до 2%.

В некоторых случаях сборщик мусора ведет себя менее беспорядочно по отношению к горутине.

Go 1.20 добавляет новый пакет runtime/coverage, содержащий API для записи данных профиля покрытия во время выполнения из долго работающих и/или серверных программ, которые не завершаются через os.Exit().

Компилятор

В Go 1.20 добавлена предварительная поддержка оптимизации на основе профиля (PGO). PGO позволяет цепочке инструментов выполнять оптимизацию для конкретных приложений и рабочих нагрузок на основе информации о профиле времени выполнения. В настоящее время компилятор поддерживает профили CPU pprof, которые можно собирать обычными средствами, такими как пакеты runtime/pprof или net/http/pprof. Чтобы включить PGO, передайте путь к файлу профиля pprof через флаг -pgo, чтобы начать сборку, как указано выше. Go 1.20 использует PGO для более агрессивного встраивания функций в местах горячих вызовов. Тесты для репрезентативного набора программ Go показывают, что включение оптимизации встраивания на основе профиля повышает производительность примерно на 3–4%. Планируется добавить больше оптимизаций на основе профилей в будущих релизах. Обратите внимание, что оптимизация на основе профилей является предварительной версией, поэтому используйте ее с осторожностью.

Компилятор Go 1.20 обновил свой внешний интерфейс, чтобы использовать новый способ обработки внутренних данных компилятора, который устраняет несколько проблем с универсальными типами и позволяет объявлять типы в универсальных функциях и методах.

Компилятор теперь по умолчанию отклоняет циклы анонимного интерфейса с ошибкой компилятора. Они возникают из-за хитрого использования встроенных интерфейсов и всегда имели тонкие проблемы с корректностью, но у разработчиков языка Go нет доказательств того, что они действительно используются на практике. Предполагая, что это изменение не повлияет на отчеты пользователей, планируется обновить спецификацию языка для Go 1.22, чтобы официально запретить их, чтобы авторы инструментов также могли прекратить их поддержку.

В Go 1.18 и 1.19 наблюдался регресс в скорости сборки, в основном из-за добавления поддержки дженериков и последующей работы. Go 1.20 повышает скорость сборки до 10 %, возвращая ее в соответствие с Go 1.17. По сравнению с Go 1.19 производительность сгенерированного кода также в целом немного улучшилась.

Линкер (компоновщик)

В Linux компоновщик теперь выбирает динамический интерпретатор для glibc или musl во время компоновки.

В Windows компоновщик Go теперь поддерживает современные наборы инструментов C на основе LLVM.

Go 1.20 использует префиксы go: и type: для символов, сгенерированных компилятором, а не go. и type. . Это позволяет избежать путаницы для пользовательских пакетов, имя которых начинается с go. . Пакет debug/gosym понимает это новое соглашение об именах для двоичных файлов, созданных с помощью Go 1.20 и новее.

Начальная загрузка (Bootstrap)

При сборке релиза Go из исходного кода и не заданном GOROOT_BOOTSTRAP предыдущие версии Go искали цепочку инструментов начальной загрузки Go 1.4 или более поздней версии в каталоге $HOME/go1.4 (%HOMEDRIVE%%HOMEPATH%\go1.4 в Windows). Go 1.18 и Go 1.19 сначала искали $HOME/go1.17 или $HOME/sdk/go1.17, а затем возвращались к $HOME/go1.4, ожидая, что Go 1.17 потребуется для использования при начальной загрузке Go 1.20. Go 1.20 требует версии Go 1.17 для начальной загрузки, но стало понятно, что следует использовать последнюю точечную версию цепочки инструментов начальной загрузки, поэтому для нее требуется Go 1.17.13. Go 1.20 ищет $HOME/go1.17.13 или $HOME/sdk/go1.17.13, прежде чем вернуться к $HOME/go1.4 (для поддержки систем, которые жестко запрограммировали путь $HOME/go1.4, но установили более новый Go туда). В будущем планируется продвигать цепочку инструментов начальной загрузки примерно раз в год, и, в частности, ожидается, что Go 1.22 потребует финальной версии Go 1.20 для начальной загрузки.

Основная библиотека


Новый пакет crypto/ecdh

Go 1.20 добавляет новый пакет crypto/ecdh для обеспечения явной поддержки обмена ключами Диффи-Хеллмана на эллиптических кривых по кривым NIST и Curve25519.

Программы должны использовать crypto/ecdh вместо функций более низкого уровня в crypto/elliptic для ECDH и сторонние модули для более продвинутых вариантов использования.

Обертывание нескольких ошибок

Go 1.20 расширяет поддержку обертывания ошибок, позволяя одной ошибке оборачивать несколько других ошибок.

Ошибка e может обернуть более одной ошибки, предоставив метод Unwrap, который возвращает []error.

Функции errors.Is и errors.As были обновлены для проверки многократно обернутых ошибок.

Функция fmt.Errorf теперь поддерживает несколько вхождений глагола форматирования %w, из-за чего она возвращает ошибку, обертывающую все эти операнды ошибок.

Новая функция errors.Join возвращает ошибку, обертывающую список ошибок.

HTTP ResponseController

Новый тип "net/http".ResponseController предоставляет доступ к расширенным функциям для каждого запроса, которые не обрабатываются интерфейсом "net/http".ResponseWriter.

Ранее были добавилены новые функции для каждого запроса, определив необязательные интерфейсы, которые может реализовать ResponseWriter, например Flusher. Эти интерфейсы не обнаруживаются и неуклюжи в использовании.

Тип ResponseController обеспечивает более понятный и удобный способ добавления элементов управления для каждого обработчика. В Go 1.20 также добавлены два таких элемента управления: SetReadDeadline и SetWriteDeadline, которые позволяют устанавливать крайние сроки чтения и записи для каждого запроса. Например:

func RequestHandler(w ResponseWriter, r *Request) {
  rc := http.NewResponseController(w)
  rc.SetWriteDeadline(time.Time{}) // отключаем Server.WriteTimeout при отправке большого ответа
  io.Copy(w, bigData)
}

Новый хук ReverseProxy Rewrite

Прокси-сервер пересылки httputil.ReverseProxy включает новую функцию хука Rewrite, заменяющую предыдущий хук Director.

Хук Rewrite принимает параметр ProxyRequest, который включает в себя как входящий запрос, полученный прокси-сервером, так и исходящий запрос, который он отправит. В отличие от хуков Director, которые работают только с исходящим запросом, это позволяет хукам Rewrite избегать определенных сценариев, когда злонамеренный входящий запрос может привести к удалению заголовков, добавленных хуком, перед пересылкой.

Метод ProxyRequest.SetURL направляет исходящий запрос в указанное место назначения и заменяет функцию NewSingleHostReverseProxy. В отличие от NewSingleHostReverseProxy, SetURL также устанавливает заголовок узла исходящего запроса.

Метод ProxyRequest.SetXForwarded устанавливает заголовки X-Forwarded-For, X-Forwarded-Host и X-Forwarded-Proto исходящего запроса. При использовании Rewrite эти заголовки не добавляются по умолчанию.

Пример хука Rewrite, использующего эти функции:

proxyHandler := &httputil.ReverseProxy{
  Rewrite: func(r *httputil.ProxyRequest) {
    r.SetURL(outboundURL) // Перенаправить запрос на исходящий URL.
    r.SetXForwarded()     //  Установить заголовки X-Forwarded-*.
    r.Out.Header.Set("X-Additional-Header", "header set by the proxy")
  },
}

ReverseProxy больше не добавляет заголовок User-Agent к пересылаемым запросам, если во входящем запросе его нет.

Мелкие изменения в библиотеке

Как всегда, в библиотеку внесены различные незначительные изменения и обновления, сделанные с учетом обещания совместимости с Go 1. Существуют также различные улучшения производительности, не перечисленные здесь.

archive/tar

Если установлена переменная среды GODEBUG=tarinsecurepath=0, метод Reader.Next теперь будет возвращать ошибку ErrInsecurePath для записи с именем файла, которое является абсолютным путем, ссылается на расположение за пределами текущего каталога, содержит недопустимые символы или (в Windows) — это зарезервированное имя, такое как NUL. В будущей версии Go небезопасные пути могут быть отключены по умолчанию.

archive/zip

Если установлена переменная окружения GODEBUG=zipinsecurepath=0, NewReader теперь будет возвращать ошибку ErrInsecurePath при открытии архива, содержащего любое имя файла, которое является абсолютным путем, ссылается на расположение за пределами текущего каталога, содержит недопустимые символы или (на Windows) — это зарезервированные имена, такие как NUL. В будущей версии Go небезопасные пути могут быть отключены по умолчанию.

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

bytes

Новые функции CutPrefix и CutSuffix похожи на TrimPrefix и TrimSuffix, но также сообщают, была ли строка обрезана.

Новая функция Clone выделяет копию байтового среза.

context

Новая функция WithCancelCause предоставляет способ отменить контекст с заданной ошибкой. Эту ошибку можно получить, вызвав новую функцию Cause.

crypto/ecdsa

При использовании поддерживаемых кривых все операции теперь выполняются за постоянное время. Это привело к увеличению процессорного времени от 5% до 30%, в основном затронув P-384 и P-521.

Новый метод PrivateKey.ECDH преобразует ecdsa.PrivateKey в ecdh.PrivateKey.

crypto/ed25519

Метод PrivateKey.Sign и функция VerifyWithOptions теперь поддерживают подписание предварительно хэшированных сообщений с помощью Ed25519ph, на что указывает Options.HashFunc, возвращающий crypto.SHA512. Кроме того, теперь они поддерживают Ed25519ctx и Ed25519ph с контекстом, указанным в новом поле Options.Context.

crypto/rsa

Новое поле OAEPOptions.MGFHash позволяет отдельно настроить хэш MGF1 для расшифровки OAEP.

crypto/rsa теперь использует новый, более безопасный бекенд с постоянным временем. Это приводит к увеличению времени выполнения ЦП для операций расшифровки примерно на 15% (RSA-2048 на amd64) до 45% (RSA-4096 на arm64) и больше на 32-разрядных архитектурах. Операции шифрования примерно в 20 раз медленнее, чем раньше (но все же в 5-10 раз быстрее, чем дешифрование). Ожидается, что производительность улучшится в будущих релизах. Программы не должны изменять или создавать вручную поля PrecomputedValues.

crypto/subtle

Новая функция XORBytes XOR объединяет два байтовых среза.

crypto/tls

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

При сбое рукопожатия из-за сбоя проверки сертификата клиент и сервер TLS теперь возвращают ошибку нового типа CertificateVerificationError, которая включает представленные сертификаты.

crypto/x509

ParsePKCS8PrivateKey и MarshalPKCS8PrivateKey теперь поддерживают ключи типа *crypto/ecdh.PrivateKey. ParsePKIXPublicKey и MarshalPKIXPublicKey теперь поддерживают ключи типа *crypto/ecdh.PublicKey. Анализ ключей кривой NIST по-прежнему возвращает значения типа *ecdsa.PublicKey и *ecdsa.PrivateKey. Используйте их новые методы ECDH для преобразования в типы crypto/ecdh.

Новая функция SetFallbackRoots позволяет программе определить набор резервных корневых сертификатов на случай, если верификатор операционной системы или стандартный корневой пакет платформы недоступен во время выполнения. Чаще всего он будет использоваться с новым пакетом golang.org/x/crypto/x509roots/fallback, который предоставит обновленный корневой пакет.

debug/elf

Попытки чтения из раздела SHT_NOBITS с помощью Section.Data или средства чтения, возвращенного Section.Open, теперь возвращают ошибку.

Дополнительные константы R_LARCH_* определены для использования с системами LoongArch.

Дополнительные константы R_PPC64_* определены для использования с перемещениями PPC64 ELFv2.

Значение константы для R_PPC64_SECTOFF_LO_DS исправлено с 61 на 62.

debug/gosym

В связи с изменением правил именования символов Go инструменты, обрабатывающие двоичные файлы Go, должны использовать пакет debug/gosym версии 1.20 для прозрачной обработки как старых, так и новых двоичных файлов.

debug/pe

Дополнительные константы IMAGE_FILE_MACHINE_RISCV* определены для использования с системами RISC-V.

encoding/binary

Функции ReadVarint и ReadUvarint теперь будут возвращать io.ErrUnexpectedEOF после чтения частичного значения, а не io.EOF.

encoding/xml

Новый метод Encoder.Close можно использовать для проверки незакрытых элементов после завершения кодирования.

Теперь декодер отклоняет имена элементов и атрибутов, содержащие более одного двоеточия, такие как <a:b:c>, а также пространства имен, которые разрешаются в пустую строку, например xmlns:a="".

Декодер теперь отклоняет элементы, которые используют разные префиксы пространства имен в открывающем и закрывающем теге, даже если оба этих префикса обозначают одно и то же пространство имен.

errors

Новая функция Join возвращает ошибку, обертывающую список ошибок.

fmt

Функция Errorf поддерживает несколько вхождений глагола формата %w, возвращая ошибку, которая разворачивается в список всех аргументов %w.

Новая функция FormatString восстанавливает директиву форматирования, соответствующую состоянию, что может быть полезно в Formatter. реализации.

go/ast

Новое поле RangeStmt.Range записывает положение ключевого слова диапазона в операторе диапазона.

В новых полях File.FileStart и File.FileEnd записывается положение начала и конца всего исходного файла.

go/token

Новый метод FileSet.RemoveFile удаляет файл из набора файлов. Долго работающие программы могут использовать это для освобождения памяти, связанной с файлами, которые им больше не нужны.

go/types

Новая функция Satisfies сообщает, удовлетворяет ли тип ограничениям. Это изменение согласуется с новой языковой семантикой, которая отличает удовлетворение ограничения от реализации интерфейса.

io

Новый OffsetWriter является оболочкой базового WriterAt и предоставляет методы Seek, Write и WriteAt, которые корректируют их эффективную позицию смещения в файле на фиксированную величину.

io/fs

Новая ошибка SkipAll немедленно, но успешно завершает WalkDir.

math/big

Широкая область применения пакета math/big и время, зависящее от ввода, делают его плохо подходящим для реализации криптографии. Пакеты криптографии в стандартной библиотеке больше не вызывают нетривиальные методы Int для входных данных, контролируемых злоумышленником. В будущем определение того, считается ли ошибка в math/big уязвимостью системы безопасности, будет зависеть от ее более широкого влияния на стандартную библиотеку.

math/rand

Пакет math/rand теперь автоматически заполняет глобальный генератор случайных чисел (используемый функциями верхнего уровня, такими как Float64 и Int) случайным значением, а функция Seed верхнего уровня устарела. Программы, которым нужна воспроизводимая последовательность случайных чисел, должны предпочитать выделять свой собственный источник случайных чисел, используя rand.New(rand.NewSource(seed)).

Программы, которым требуется более раннее последовательное глобальное заполнение, могут установить GODEBUG=randautoseed=0 в своей среде.

Функция чтения верхнего уровня устарела. Почти во всех случаях лучше подходит crypto/rand.Read.

mime

Функция ParseMediaType теперь позволяет дублировать имена параметров, если значения имен совпадают.

mime/multipart

Методы типа Reader теперь оборачивают ошибки, возвращаемые базовым io.Reader.

net

Функция LookupCNAME теперь последовательно возвращает содержимое записи CNAME, если она существует. Раньше в системах Unix и при использовании чистого преобразователя Go функция LookupCNAME возвращала ошибку, если запись CNAME ссылалась на имя без записи A, AAAA или CNAME. Это изменение изменяет LookupCNAME, чтобы оно соответствовало предыдущему поведению в Windows, позволяя успешно выполнять LookupCNAME всякий раз, когда CNAME существует.

Interface.Flags теперь включает новый флаг FlagRunning, указывающий на операционно активный интерфейс. Для интерфейса, настроенного административно, но неактивного (например, из-за того, что сетевой кабель не подключен), будет установлен FlagUp, но не FlagRunning.

Новое поле Dialer.ControlContext содержит функцию обратного вызова, аналогичную существующему хуку Dialer.Control, который дополнительно принимает контекст набора в качестве параметра. Управление игнорируется, если ControlContext не равен нулю.

Сопоставитель Go DNS распознает вариант сопоставителя trust-ad. Когда в resolv.conf установлены параметры trust-ad, преобразователь Go будет устанавливать бит AD в DNS-запросах. Преобразователь не использует бит AD в ответах.

Разрешение DNS обнаружит изменения в /etc/nsswitch.conf и перезагрузит файл при его изменении. Проверки выполняются не чаще одного раза каждые пять секунд, что соответствует предыдущей обработке файлов /etc/hosts и /etc/resolv.conf.

net/http

Функция ResponseWriter.WriteHeader теперь поддерживает отправку кодов состояния 1xx.

Новый параметр конфигурации Server.DisableGeneralOptionsHandler позволяет отключить обработчик OPTIONS * по умолчанию.

Новый хук Transport.OnProxyConnectResponse вызывается, когда Transport получает HTTP-ответ от прокси-сервера на запрос CONNECT.

HTTP-сервер теперь принимает запросы HEAD, содержащие тело, а не отклоняет их как недействительные.

Ошибки потока HTTP/2, возвращаемые функциями net/http, могут быть преобразованы в golang.org/x/net/http2.StreamError с помощью errors.As.

Начальные и конечные пробелы удаляются из имен файлов cookie, а не отклоняются как недействительные. Например, параметр файла cookie
"имя =значение" теперь принимается как параметр "имя" файла cookie.

net/netip

Новые функции IPv6LinkLocalAllRouters и IPv6Loopback являются эквивалентами net/netip функций net.IPv6loopback и net.IPv6linklocalallrouters.

os

В Windows имя NUL больше не рассматривается как особый случай в Mkdir и Stat.

В Windows File.Stat теперь использует дескриптор файла для получения атрибутов, когда файл является каталогом. Ранее он использовал путь, переданный в Open, который больше не может быть файлом, представленным дескриптором файла, если файл был перемещен или заменен. Это изменение изменяет Open для открытия каталогов без доступа FILE_SHARE_DELETE, что соответствует поведению обычных файлов.

В Windows File.Seek теперь поддерживает поиск в начале каталога.

os/exec

Новые поля Cmd Cancel и WaitDelay определяют поведение Cmd, когда связанный с ним контекст отменяется или его процесс завершается, а каналы ввода-вывода остаются открытыми дочерним процессом.

path/filepath

Новая ошибка SkipAll немедленно, но успешно завершает Walk.

Новая функция IsLocal сообщает, является ли путь лексически локальным для каталога. Например, если IsLocal(p) имеет значение true, то Open(p) будет ссылаться на файл, который лексически находится внутри поддерева с корнем в текущем каталоге.

reflect

Новые методы Value.Comparable и Value.Equal можно использовать для сравнения двух значений на предмет равенства. Comparable сообщает, является ли Equal допустимой операцией для данного приемника Value.

Новый метод Value.Grow расширяет срез, чтобы гарантировать место для других n элементов.

Новый метод Value.SetZero задает нулевое значение для своего типа.

Go 1.18 представил методы Value.SetIterKey и Value.SetIterValue. Это оптимизации: v.SetIterKey(it) должен быть эквивалентен v.Set(it.Key()). Реализации ошибочно пропускали проверку на использование неэкспортированных полей, которая присутствовала в неоптимизированных формах. Go 1.20 исправляет эти методы, чтобы включить проверку неэкспортированных полей.

regexp

Go 1.19.2 и Go 1.18.7 включали исправление безопасности для синтаксического анализатора регулярных выражений, заставляющее его отклонять очень большие выражения, которые потребляли бы слишком много памяти. Поскольку релизы исправлений Go не вводят новый API, синтаксический анализатор в этом случае вернул синтаксис.ErrInternalError. В Go 1.20 добавлена более конкретная ошибка syntax.ErrLarge, которую теперь возвращает синтаксический анализатор.

runtime/cgo

Go 1.20 добавляет новый тип маркера Incomplete. Код, сгенерированный cgo, будет использовать cgo.Incomplete для обозначения неполного типа C.

runtime/metrics

Go 1.20 добавляет новые поддерживаемые метрики, включая текущую настройку GOMAXPROCS (/sched/gomaxprocs:threads), количество выполненных вызовов cgo (/cgo/go-to-c-calls:calls), общее время блокировки мьютекса (/sync/mutex/wait/total:seconds) и различные меры времени, потраченного на сборку мусора.

Метрики гистограмм на основе времени теперь менее точны, но занимают гораздо меньше памяти.

runtime/pprof

Образцы профиля мьютекса теперь предварительно масштабируются, что устраняет проблему, из-за которой старые образцы профиля мьютекса масштабировались неправильно, если частота дискретизации изменялась во время выполнения.

Профили, собранные в Windows, теперь включают информацию о сопоставлении памяти, которая устраняет проблемы с символизацией для позиционно-независимых двоичных файлов.

runtime/trace

Фоновая очистка сборщика мусора теперь завершается реже, что приводит к меньшему количеству посторонних событий в трассировках выполнения.

strings

Новые функции CutPrefix и CutSuffix похожи на TrimPrefix и TrimSuffix, но также сообщают, была ли строка обрезана.

sync

Новые методы Map Swap, CompareAndSwap и CompareAndDelete позволяют автоматически обновлять существующие записи карты.

syscall

Во FreeBSD прокладки совместимости, необходимые для FreeBSD 11 и более ранних версий, были удалены.

В Linux определены дополнительные константы CLONE_* для использования с полем SysProcAttr.Cloneflags.

В Linux новые поля SysProcAttr.CgroupFD и SysProcAttr.UseCgroupFD позволяют поместить дочерний процесс в определенную контрольную группу.

testing

Новый метод B.Elapsed сообщает о текущем прошедшем времени эталонного теста, что может быть полезно для расчета показателей для отчета с помощью ReportMetric.

time

Новые константы макета времени DateTime, DateOnly и TimeOnly предоставляют имена для трех наиболее распространенных строк шаблона, используемых в обзоре общедоступного исходного кода Go.

Новый метод Time.Compare сравнивает два времени.

Parse теперь игнорирует точность менее наносекунды в своих входных данных, вместо того, чтобы сообщать об этих цифрах как об ошибке.

Метод Time.MarshalJSON теперь более строго соответствует RFC 3339.

unicode/utf16

Новая функция AppendRune добавляет кодировку UTF-16 данной руны к фрагменту uint16, аналогично utf8.AppendRune.


Читайте также:


четверг, 22 сентября 2022 г.

Релиз Go 1.19

Релиз Go, версия 1.19, выходит через пять месяцев после Go 1.18. Большинство его изменений касаются реализации цепочки инструментов, среды выполнения и библиотек. Как всегда, выпуск поддерживает обещание совместимости Go 1. Ожидается, что почти все программы Go продолжат компилироваться и работать, как и раньше.

Изменения в языке

Есть только одно небольшое изменение в языке, очень небольшое исправление области действия параметров типа в объявлениях методов. Существующие программы не затрагиваются.

Модель памяти

Модель памяти Go была пересмотрена, чтобы привести Go в соответствие с моделью памяти, используемой в C, C++, Java, JavaScript, Rust и Swift. Go предоставляет только последовательно согласованные атомарные элементы, а не какие-либо более простые формы, встречающиеся в других языках. Наряду с обновлением модели памяти в Go 1.19 представлены новые типы в пакете sync/atomic, которые упрощают использование атомарных значений, таких как atomic.Int64 и atomic.Pointer[T].

Порты

64-битная версия LoongArch

Go 1.19 добавляет поддержку 64-битной архитектуры Loongson LoongArch для Linux (GOOS=linux, GOARCH=loong64). Реализованный ABI — LP64D. Минимальная поддерживаемая версия ядра — 5.19.

Обратите внимание, что большинство существующих коммерческих дистрибутивов Linux для LoongArch поставляются со старыми ядрами с исторически несовместимым системным вызовом ABI. Скомпилированные двоичные файлы не будут работать в этих системах, даже если они статически связаны. Пользователи таких неподдерживаемых систем ограничены пакетом Go из дистрибутива.

RISC-V

Порт riscv64 теперь поддерживает передачу аргументов функции и результата с использованием регистров. Сравнительный анализ показывает типичное повышение производительности на 10% и более на riscv64.

Инструменты

Комментарии к документу

В Go 1.19 добавлена поддержка ссылок, списков и более четких заголовков в комментариях к документам. В рамках этого изменения gofmt теперь переформатирует комментарии к документам, чтобы сделать их отображаемый смысл более ясным.В качестве еще одной части этого изменения новый пакет go/doc/comment обеспечивает синтаксический анализ и переформатирование комментариев к документам, а также поддержку их преобразования в HTML, Markdown и текст.

Новое ограничение сборки unix

Unix-ограничение сборки теперь распознается в строках //go:build. Ограничение выполняется, если целевая операционная система, также известная как GOOS, является Unix или Unix-подобной системой. Для версии 1.19 допустимо, если GOOS является одним из следующих: aix, android, darwin, dragonfly, freebsd, hurd, illumos, ios, linux, netbsd, openbsd или solaris. В будущих релизах ограничение unix может соответствовать дополнительным вновь поддерживаемым операционным системам.

Команда go

Флаг -trimpath, если он установлен, теперь включается в настройки сборки, проставленные в двоичных файлах Go командой go build, и может быть проверен с помощью go version -m или debug.ReadBuildInfo.

go generate теперь устанавливает переменную среды GOROOT явно в среде генератора, так что генераторы могут найти правильный GOROOT, даже если он собран с -trimpath.

go test и go generate теперь размещает GOROOT/bin в начало PATH, используемого для подпроцесса, поэтому тесты и генераторы, выполняющие команду go, разрешат ее в один и тот же GOROOT.

go env теперь заключает в кавычки записи, содержащие пробелы в отчетных переменных CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS, CGO_FFLAGS, CGO_LDFLAGS и GOGCCFLAGS.

go list -json теперь принимает список полей JSON, разделенных запятыми, для заполнения. Если указан список, выходные данные JSON будут включать только эти поля, и список перехода может избежать работы по вычислению полей, которые не включены. В некоторых случаях это может подавить ошибки, о которых в противном случае сообщалось бы.

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

Ветеринар (Vet)

vet чекер "errorsas" теперь сообщает когда errors.As вызывается со вторым аргументом типа *error, что является распространенной ошибкой.

Среда выполнения (Runtime)

Среда выполнения теперь включает поддержку мягкого ограничения памяти. Это ограничение памяти включает кучу (heap) Go и всю другую память, управляемую средой выполнения, и исключает внешние источники памяти, такие как сопоставления самого двоичного файла, память, управляемую на других языках, и память, удерживаемую операционной системой от имени программы Go. Этим ограничением можно управлять с помощью runtime/debug.SetMemoryLimit или эквивалентной переменной среды GOMEMLIMIT. Ограничение работает в сочетании с runtime/debug.SetGCPercent / GOGC и будет соблюдаться, даже если GOGC=off, позволяя программам Go всегда максимально использовать свой лимит памяти, в некоторых случаях повышая эффективность использования ресурсов. Обратите внимание, что небольшие ограничения памяти, порядка десятков мегабайт или меньше, с меньшей вероятностью будут соблюдаться из-за внешних факторов задержки, таких как планирование ОС. Большие пределы памяти, порядка сотен мегабайт и более, стабильны и готовы к работе.

Чтобы ограничить последствия перегрузки GC, когда размер динамической кучи программы приближается к пределу программной памяти, среда выполнения Go также пытается ограничить общее использование ЦП GC до 50%, исключая время простоя, предпочитая использовать больше памяти, а не предотвращая выполнение приложения. На практике ожидается, что это ограничение будет играть роль только в исключительных случаях, и новая метрика среды выполнения /gc/limiter/last-enabled:gc-cycle сообщает, когда это произошло в последний раз.

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

Среда выполнения теперь будет выделять начальные стеки горутин на основе исторического среднего использования стека горутин. Это позволяет избежать некоторого раннего роста стека и копирования, необходимого в среднем случае, в обмен на не более чем двукратное неиспользованное пространство на горутинах ниже среднего.

В операционных системах Unix программы Go, которые импортируют пакет os, теперь автоматически увеличивают лимит открытых файлов (RLIMIT_NOFILE) до максимально допустимого значения; то есть они изменяют мягкое ограничение, чтобы оно соответствовало жесткому ограничению. Это исправляет искусственно заниженные ограничения, установленные в некоторых системах для совместимости с очень старыми программами на C, использующими системный вызов select. Это ограничение не помогает программам Go, и вместо этого даже простые программы, такие как gofmt, часто не имеют файловых дескрипторов в таких системах при параллельной обработке большого количества файлов. Одним из последствий этого изменения является то, что программы Go, которые, в свою очередь, выполняют очень старые программы C в дочерних процессах, могут запускать эти программы со слишком высоким ограничением. Это можно исправить, установив жесткое ограничение перед вызовом программы Go.

Неисправимые фатальные ошибки (такие как одновременная запись карты или разблокировка разблокированных мьютексов) теперь печатают более простую трассировку, исключая метаданные времени выполнения (эквивалент фатальной паники), если только GOTRACEBACK не установлено значение system или crash. Внутренние трассировки неустранимых ошибок во время выполнения всегда включают полные метаданные независимо от значения GOTRACEBACK.

В ARM64 была добавлена ​​поддержка вызовов функций, введенных отладчиком, что позволяет пользователям вызывать функции из своего двоичного файла в интерактивном сеансе отладки при использовании отладчика, обновленного для использования этой функции.

Поддержка очистки адресов, добавленная в Go 1.18, теперь более точно обрабатывает аргументы функций и глобальные переменные.

Компилятор

Теперь компилятор использует таблицу переходов для реализации больших целочисленных и строковых операторов переключения. Улучшения производительности оператора switch различаются, но могут быть на порядка 20% быстрее. (только GOARCH=amd64 и GOARCH=arm64)

Компилятору Go теперь требуется флаг -p=importpath для создания связываемого объектного файла. Это уже предоставлено командой go и Bazel. Любые другие системы сборки, которые напрямую вызывают компилятор Go, также должны убедиться, что они передают этот флаг.

Компилятор Go больше не поддерживает флаг -importmap. Системы сборки, которые напрямую вызывают компилятор Go, должны вместо этого использовать флаг -importcfg.

Ассемблер

Как и компилятору, ассемблеру теперь требуется флаг -p=importpath для создания компоновочного объектного файла. Это уже предоставлено командой go. Любые другие системы сборки, которые напрямую вызывают ассемблер Go, также должны убедиться, что они передают этот флаг.

Линкер

На платформах ELF компоновщик теперь создает сжатые разделы DWARF в стандартном формате gABI (SHF_COMPRESSED) вместо устаревшего формата .zdebug.

Основная библиотека

Новые атомарные типы

Пакет sync/atomic определяет новые атомарные типы Bool, Int32, Int64, Uint32, Uint64, Uintptr и Pointer. Эти типы скрывают базовые значения, поэтому все обращения вынуждены использовать атомарные API. Pointer также избавляет от необходимости конвертировать в unsafe.Pointer в местах вызовов. Int64 и Uint64 автоматически выравниваются по 64-битным границам в структурах и выделенных данных даже в 32-битных системах.

PATH поиск

Command и LookPath больше не позволяют находить результаты поиска PATH относительно текущего каталога. Это устраняет общий источник проблем с безопасностью, но может также нарушить работу существующих программ, которые зависят от использования, скажем, exec.Command("prog") для запуска двоичного файла с именем prog (или, в Windows, prog.exe) в текущем каталоге.

В Windows Command и LookPath теперь учитывают переменную среды NoDefaultCurrentDirectoryInExePath, что позволяет отключить неявный поиск “.” по умолчанию в поиске PATH в системах Windows.

Мелкие изменения в библиотеке

Как всегда, в библиотеку внесены различные незначительные изменения и обновления, сделанные с учетом обещания совместимости с Go 1. Существуют также различные улучшения производительности, не перечисленные здесь.

archive/zip

Reader теперь игнорирует данные, отличные от ZIP, в начале ZIP-файла, что соответствует большинству других реализаций. Это необходимо, помимо прочего, для чтения некоторых файлов Java JAR.

crypto/elliptic

Работа с недопустимыми точками кривой (теми, для которых метод IsOnCurve возвращает false и которые никогда не возвращаются Unmarshal или методом Curve, работающим с допустимой точкой) всегда было неопределенным поведением и может привести к атакам восстановления ключа. Если для Marshal, MarshalCompressed, Add, Double или ScalarMult указана недопустимая точка, они теперь будут паниковать.

Операции ScalarBaseMult с кривыми P224, P384 и P521 теперь выполняются до трех раз быстрее, что приводит к аналогичному ускорению некоторых операций ECDSA. Общая (не оптимизированная для платформы) реализация P256 была заменена реализацией, полученной из официально проверенной модели; это может привести к значительному замедлению работы на 32-разрядных платформах.

crypto/rand

Read больше не буферизует случайные данные, полученные от операционной системы, между вызовами. Приложения, которые выполняют много небольших операций чтения с высокой частотой, могут по соображениям производительности обернуть Reader в bufio.Reader, стараясь использовать io.ReadFull, чтобы гарантировать отсутствие частичных чтений.

В Plan 9 функция Read была повторно реализована, заменив алгоритм ANSI X9.31 на генератор быстрого стирания ключей.

Реализация Prime была изменена, чтобы использовать только выборку отклонения, что устраняет предвзятость при создании небольших простых чисел в некриптографических контекстах, устраняет одну возможную незначительную утечку времени и лучше согласовывает поведение с BoringSSL, при этом упрощая реализацию. Это изменение создает разные выходные данные для данного случайного исходного потока по сравнению с предыдущей реализацией, что может нарушить тесты, написанные с ожиданием определенных результатов от определенных детерминированных случайных источников. Чтобы предотвратить такие проблемы в будущем, реализация теперь преднамеренно недетерминирована по отношению к входному потоку.

crypto/tls

Опция GODEBUG tls10default=1 удалена. По-прежнему можно включить TLS 1.0 на стороне клиента, установив Config.MinVersion.

Сервер и клиент TLS теперь отклоняют повторяющиеся расширения в рукопожатиях TLS, как того требуют RFC 5246, раздел 7.4.1.4 и RFC 8446, раздел 4.2.

crypto/x509

CreateCertificate больше не поддерживает создание сертификатов с SignatureAlgorithm, для которого задано значение MD5WithRSA.

CreateCertificate больше не принимает отрицательные серийные номера.

CreateCertificate больше не будет выдавать пустую SEQUENCE, если созданный сертификат не имеет расширений.

Удаление GODEBUG optionx509sha1=1, первоначально запланированное для Go 1.19, было перенесено на будущий релиз. Приложения, использующие его, должны работать при миграции. Практические атаки на SHA-1 были продемонстрированы с 2017 года, а общедоступные доверенные центры сертификации не выпускали сертификаты SHA-1 с 2015 года.

ParseCertificate и ParseCertificateRequest теперь отклоняют сертификаты и CSR, которые содержат повторяющиеся расширения.

Новые методы CertPool.Clone и CertPool.Equal позволяют клонировать CertPool и проверять эквивалентность двух CertPools соответственно.

Новая функция ParseRevocationList обеспечивает более быстрый и безопасный анализатор CRL, который возвращает RevocationList. При разборе CRL также заполняются новые поля RevocationList RawIssuer, Signature, AuthorityKeyId и Extensions, которые игнорируются CreateRevocationList.

Новый метод RevocationList.CheckSignatureFrom проверяет, является ли подпись в CRL действительной подписью сертификата.

Функции ParseCRL и ParseDERCRL теперь объявлены устаревшими в пользу ParseRevocationList. Метод Certificate.CheckCRLSignature устарел в пользу RevocationList.CheckSignatureFrom.

Построитель пути Certificate.Verify был переработан и теперь должен создавать более качественные цепочки и/или быть более эффективным в сложных сценариях. Ограничения имен теперь также применяются к неконечным сертификатам.

crypto/x509/pkix

Типы CertificateList и TBSCertificateList устарели. Вместо этого следует использовать новую функциональность crypto/x509 CRL.

debug/elf

Новые константы EM_LOONGARCH и R_LARCH_* поддерживают порт loong64.

debug/pe

Новый метод File.COFFSymbolReadSectionDefAux, возвращающий COFFSymbolAuxFormat5, обеспечивает доступ к информации COMDAT в разделах PE-файла. Они поддерживаются новыми константами IMAGE_COMDAT_* и IMAGE_SCN_*.

encoding/binary

Новый интерфейс AppendByteOrder предоставляет эффективные методы добавления uint16, uint32 или uint64 к срезу байтов. BigEndian и LittleEndian теперь реализуют этот интерфейс.

Точно так же новые функции AppendUvarint и AppendVarint являются эффективными версиями добавления функций PutUvarint и PutVarint.

encoding/csv

Новый метод Reader.InputOffset сообщает о текущей позиции ввода средства чтения в виде смещения в байтах, аналогично Decoder.InputOffset в encoding/json.

encoding/xml

Новый метод Decoder.InputPos сообщает текущую позицию ввода считывателя в виде строки и столбца, аналогично Decoder.FieldPos из encoding/csv.

flag

Новая функция TextVar определяет флаг со значением, реализующим encoding.TextUnmarshaler, что позволяет переменным флага командной строки иметь такие типы, как big.Int, netip.Addr и time.Time.

fmt

Новые функции Append, Appendf и Appendln добавляют отформатированные данные в байтовые срезы.

go/parser

Синтаксический анализатор теперь распознает ~x как унарное выражение с оператором token.TILDE, что позволяет лучше устранять ошибки, когда ограничение типа, такое как ~int, используется в неправильном контексте.

go/types

Новые методы Func.Origin и Var.Origin возвращают соответствующий Object универсального типа для синтетических объектов Func и Var, созданных во время создания экземпляра типа.

Больше невозможно создавать бесконечное количество различных, но идентичных экземпляров именованного типа с помощью рекурсивных вызовов Named.Underlying или Named.Method.

hash/maphash

Новые функции Bytes и String обеспечивают эффективный способ хеширования одного байтового среза или строки. Они эквивалентны использованию более общего хэша с одной записью, но позволяют избежать накладных расходов на настройку для небольших входных данных.

html/template

Тип FuncMap теперь является псевдонимом для FuncMap пакета text/template вместо собственного именованного типа. Это позволяет писать код, который работает с FuncMap при любых настройках.

image/draw

Рисование с оператором Src сохраняет цвета без предварительного умножения альфа-канала, когда целевое и исходное изображения оба имеют формат image.NRGBA или оба изображения image.NRGBA64. Это отменяет изменение поведения, случайно внесенное оптимизацией библиотеки Go 1.18; код теперь соответствует поведению в Go 1.17 и более ранних версиях.

io

Результат NopCloser теперь реализует WriterTo всякий раз, когда это происходит на входе.

Результат MultiReader теперь безоговорочно реализует WriterTo. Если какое-либо базовое средство чтения не реализует WriterTo, оно моделируется соответствующим образом.

mime

Только в Windows пакет mime теперь игнорирует запись реестра о том, что расширение .js должно иметь тип MIME text/plain. Это распространенная непреднамеренная неправильная настройка в системах Windows. В результате .js будет иметь тип MIME text/javascript по умолчанию; кодировка=utf-8. Приложения, которые ожидают text/plain в Windows, теперь должны явно вызывать AddExtensionType.

net

Чистый преобразователь Go теперь будет использовать EDNS(0) для включения рекомендуемой максимальной длины ответного пакета, что позволяет ответным пакетам содержать до 1232 байт (ранее максимальное значение составляло 512). В том маловероятном случае, если это вызовет проблемы с локальным распознавателем DNS, установка переменной среды GODEBUG=netdns=cgo для использования распознавателя на основе cgo должна работать.

Когда функция или метод пакета net возвращает ошибку "I/O timeout", эта ошибка теперь будет удовлетворять errors.Is(err, context.DeadlineExceeded). Когда функция пакета net возвращает ошибку "operation was canceled" ("операция была отменена"), эта ошибка теперь удовлетворяет errors.Is(err, context.Canceled). Эти изменения предназначены для того, чтобы упростить тестирование кода в случаях, когда отмена контекста или таймаут приводят к тому, что функция или метод пакета net возвращает ошибку, сохраняя при этом обратную совместимость для сообщений об ошибках.

Resolver.PreferGo теперь реализован в Windows и Plan 9. Ранее он работал только на платформах Unix. В сочетании с Dialer.Resolver и Resolver.Dial теперь можно писать переносимые программы и контролировать все поиски DNS-имен при подключении.

Пакет net теперь имеет начальную поддержку тега сборки netgo в Windows. При использовании пакет использует DNS-клиент Go (используемый Resolver.PreferGo) вместо того, чтобы запрашивать у Windows результаты DNS. Однако вышестоящий DNS-сервер, который он обнаруживает из Windows, может быть еще неправильным со сложными системными сетевыми конфигурациями.

net/http

ResponseWriter.WriteHeader теперь поддерживает отправку определяемых пользователем информационных заголовков 1xx.

io.ReadCloser, возвращаемый MaxBytesReader, теперь будет возвращать определенный тип ошибки MaxBytesError при превышении предела чтения.

HTTP-клиент будет обрабатывать ответ 3xx без заголовка Location, возвращая его вызывающей стороне, а не рассматривая его как ошибку.

net/url

Новая функция JoinPath и метод URL.JoinPath создают новый URL-адрес путем присоединения к списку элементов пути.

Тип URL-адреса теперь различает URL-адреса без полномочий и URL-адреса с пустыми полномочиями. Например, http:///path имеет пустые права доступа (хост), а в http:/path их нет.

Новое поле URL-адреса OmitHost имеет значение true, если URL-адрес имеет пустые полномочия.

os/exec

Cmd с непустым полем Dir и nil Env теперь неявно устанавливает переменную среды PWD для подпроцесса в соответствие с Dir.

Новый метод Cmd.Environ сообщает о среде, которая будет использоваться для запуска команды, включая неявно заданную переменную PWD.

reflect

Метод Value.Bytes теперь принимает адресные массивы в дополнение к срезам.

Методы Value.Len и Value.Cap теперь успешно работают с указателем на массив и возвращают длину этого массива, чтобы соответствовать тому, что делают встроенные функции len и cap.

regexp/syntax

Go 1.18 релиз-кандидат 1, Go 1.17.8 и Go 1.16.15 включали исправление безопасности для синтаксического анализатора регулярных выражений, заставляющее его отклонять очень глубоко вложенные выражения. Поскольку релизы исправлений Go не вводят новый API, синтаксический анализатор в этом случае вернул синтаксис.ErrInternalError. В Go 1.19 добавлена ​​более конкретная ошибка синтаксис.ErrNestingDepth, которую теперь возвращает синтаксический анализатор.

runtime

Функция GOROOT теперь возвращает пустую строку (вместо "go"), когда бинарный файл был собран с установленным флагом -trimpath и переменная GOROOT не установлена в среде процесса.

runtime/metrics

Новая метрика /sched/gomaxprocs:threads сообщает о текущем значении runtime.GOMAXPROCS.

Новая метрика /cgo/go-to-c-calls:calls сообщает об общем количестве вызовов, сделанных с Go на C. Эта метрика идентична функции runtime.NumCgoCall.

Новая метрика /gc/limiter/last-enabled:gc-cycle сообщает о последнем цикле сборки мусора, когда был включен ограничитель ЦП сборщика мусора.

runtime/pprof

Время пауз Stop-the-world было значительно сокращено при сборе профилей goroutine, что уменьшило общее влияние задержки на приложение.

MaxRSS теперь сообщается в профилях кучи для всех операционных систем Unix (ранее сообщалось только для GOOS=android, darwin, ios и linux).

runtime/race

Детектор гонок был обновлен для использования санитайзера потоков версии v3 на всех поддерживаемых платформах, кроме windows/amd64 и openbsd/amd64, которые остаются на версии v2. По сравнению с v2, теперь он обычно в 1,5–2 раза быстрее, использует вдвое меньше памяти и поддерживает неограниченное количество горутин. В Linux детектор гонки теперь требует как минимум glibc версии 2.17 и GNU binutils 2.26.

Детектор гонки теперь поддерживается на GOARCH=s390x.

Поддержка детектора гонки для openbsd/amd64 была удалена из вышестоящего санитайзера потоков, поэтому вряд ли оно когда-либо будет обновлено с версии v2.

runtime/trace

Если трассировка и профилировщик ЦП включены одновременно, трассировка выполнения включает образцы профиля ЦП как мгновенные события.

sort

Алгоритм сортировки был переписан для использования быстрой сортировки без шаблонов, которая работает быстрее в некоторых распространенных сценариях.

Новая функция Find похожа на Search, но часто проще в использовании: она возвращает дополнительное логическое значение, сообщающее, найдено ли равное значение.

strconv

Quote и связанные с ней функции теперь указывают руну U+007F как \x7f, а не \u007f, для совместимости с другими значениями ASCII.

syscall

На PowerPC (GOARCH=ppc64, ppc64le) Syscall, Syscall6, RawSyscall и RawSyscall6 теперь всегда возвращают 0 для возвращаемого значения r2 вместо неопределенного значения.

В AIX и Solaris теперь определен Getrusage.

time

Новый метод Duration.Abs предоставляет удобный и безопасный способ получения абсолютного значения длительности путем преобразования −2⁶³ в 2⁶³−1. (Этот пограничный случай может произойти в результате вычитания недавнего времени из нулевого времени.)

Новый метод Time.ZoneBounds возвращает время начала и окончания часового пояса, действующего в данный момент времени. Его можно использовать в цикле для перечисления всех известных переходов часовых поясов в заданном месте.


Читайте также:


понедельник, 12 сентября 2022 г.

Начало работы с фаззингом в Golang

В этом руководстве представлены основы фаззинга в Go. При фаззинге случайные данные обрабатываются для вашего теста в попытке найти уязвимости или входные данные, вызывающие сбой. Некоторыми примерами уязвимостей, которые можно найти с помощью фаззинга, являются SQL-инъекции, переполнение буфера, отказ в обслуживании и атаки с использованием межсайтовых сценариев.

В этом руководстве вы напишете fuzz тест для простой функции, запустите команду go, отладите и исправите проблемы в коде.

Предпосылки

  • Установленный Go 1.18 или более поздней версии.
  • Инструмент для редактирования вашего кода. Любой текстовый редактор, который у вас есть, будет работать нормально.
  • Командный терминал. Go хорошо работает с любым терминалом в Linux и Mac, а также с PowerShell или cmd в Windows.
  • Среда, поддерживающая фаззинг. В настоящее время фаззинг с инструментами покрытия доступен только для архитектур AMD64 и ARM64.

Создайте папку для своего кода

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

1. Откройте командную строку и перейдите в свой домашний каталог.

В Linux или Mac:

$ cd

В Windows:

C:\> cd %HOMEPATH%

В остальной части руководства в качестве подсказки будет отображаться символ $. Используемые вами команды будут работать и в Windows.

2. В командной строке создайте каталог для своего кода с именем fuzz.

$ mkdir fuzz
$ cd fuzz

3. Создайте модуль для хранения вашего кода.

Запустите команду go mod init, указав путь к модулю вашего нового кода.

$ go mod init example/fuzz
go: creating new go.mod: module example/fuzz

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

Далее вы добавите простой код для переворачивания строки, который мы позже фаззим.

Добавьте код для тестирования

На этом шаге вы добавите функцию для обращения строки.

Напишите код

1. Используя текстовый редактор, создайте файл с именем main.go в каталоге fuzz.

2. В файл main.go в верхней части файла вставьте следующее объявление пакета.

package main

Автономная программа (в отличие от библиотеки) всегда находится в пакете main.

3. Под объявлением пакета вставьте следующее объявление функции.

func Reverse(s string) string {
    b := []byte(s)
    for i, j := 0, len(b)-1; i < len(b)/2; i, j = i+1, j-1 {
        b[i], b[j] = b[j], b[i]
    }
    return string(b)
}

Эта функция примет строку, обработает ее побайтно и в конце вернет перевернутую строку.

Примечание. Этот код основан на функции stringutil.Reverse из golang.org/x/example.

4. В верхней части main.go, под объявлением пакета, вставьте следующую main функцию, чтобы инициализировать строку, перевернуть ее, распечатать вывод и повторить.

func main() {
    input := "The quick brown fox jumped over the lazy dog"
    rev := Reverse(input)
    doubleRev := Reverse(rev)
    fmt.Printf("original: %q\n", input)
    fmt.Printf("reversed: %q\n", rev)
    fmt.Printf("reversed again: %q\n", doubleRev)
}

Эта функция выполнит несколько Reverse операций, а затем выведет вывод в командную строку. Это может быть полезно для просмотра кода в действии и для отладки.

5. main функция использует пакет fmt, поэтому вам нужно будет его импортировать.

Первые строки кода должны выглядеть так:

package main

import "fmt"

Запустите код

Из командной строки в каталоге, содержащем main.go, запустите код.

$ go run .
original: "The quick brown fox jumped over the lazy dog"
reversed: "god yzal eht revo depmuj xof nworb kciuq ehT"
reversed again: "The quick brown fox jumped over the lazy dog"

Вы можете увидеть исходную строку, результат ее обращения, затем результат повторного обращения, который эквивалентен оригиналу.

Теперь, когда код работает, пришло время его протестировать.

Добавить модульный (юнит) тест

На этом шаге вы напишете базовый модульный тест для функции Reverse.

Напишите код

1. Используя текстовый редактор, создайте файл с именем reverse_test.go в каталоге fuzz.

2. Вставьте следующий код в reverse_test.go.

package main

import (
    "testing"
)

func TestReverse(t *testing.T) {
    testcases := []struct {
        in, want string
    }{
        {"Hello, world", "dlrow ,olleH"},
        {" ", " "},
        {"!12345", "54321!"},
    }
    for _, tc := range testcases {
        rev := Reverse(tc.in)
        if rev != tc.want {
                t.Errorf("Reverse: %q, want %q", rev, tc.want)
        }
    }
}

Этот простой тест подтвердит, что перечисленные входные строки будут правильно инвертированы.

Запустите код

Запустите модульный тест, используя go test

$ go test
PASS
ok      example/fuzz  0.013s

Далее вы измените модульный тест на fuzz тест.

Добавить fuzz тест

Модульный тест имеет ограничения, а именно то, что каждый вход должен быть добавлен в тест разработчиком. Одним из преимуществ фаззинга является то, что он предоставляет входные данные для вашего кода и может выявить крайние случаи, которых не достигли созданные вами тестовые случаи.

В этом разделе вы преобразуете модульный тест в fuzz тест, чтобы вы могли генерировать больше входных данных с меньшими затратами!

Обратите внимание, что вы можете хранить модульные тесты, бенчмарки и fuzz тесты в одном и том же файле *_test.go, но в этом примере вы преобразуете модульный тест в fuzz тест.

Напишите код

В текстовом редакторе замените модульный тест в reverse_test.go следующим fuzz тестом.

func FuzzReverse(f *testing.F) {
    testcases := []string{"Hello, world", " ", "!12345"}
    for _, tc := range testcases {
        f.Add(tc)  // Используйте f.Add для предоставления начального корпуса (seed corpus)
    }
    f.Fuzz(func(t *testing.T, orig string) {
        rev := Reverse(orig)
        doubleRev := Reverse(rev)
        if orig != doubleRev {
            t.Errorf("Before: %q, after: %q", orig, doubleRev)
        }
        if utf8.ValidString(orig) && !utf8.ValidString(rev) {
            t.Errorf("Reverse produced invalid UTF-8 string %q", rev)
        }
    })
}

У фаззинга также есть несколько ограничений. В своем модульном тесте вы можете предсказать ожидаемый результат функции Reverse и убедиться, что фактический результат соответствует этим ожиданиям.

Например, в тестовом случае Reverse("Hello, world") модульный тест определяет результат как "dlrow,olleH".

При фаззинге вы не можете предсказать ожидаемый результат, так как у вас нет контроля над входными данными.

Однако есть несколько свойств функции Reverse, которые вы можете проверить с помощью fuzz теста. В этом fuzz тесте проверяются два свойства:

  • Двойное обращение строки сохраняет исходное значение
  • Перевернутая строка сохраняет свое состояние как действительное UTF-8.

Обратите внимание на синтаксические различия между модульным тестом и fuzz тестом:

  • Функция начинается с FuzzXxx вместо TestXxx и принимает *testing.F вместо *testing.T.
  • Там, где вы ожидаете увидеть выполнение t.Run, вместо этого вы видите f.Fuzz, который принимает целевую функцию фаззинга с параметрами *testing.T и типами, подлежащими фаззингу. Входные данные из вашего модульного теста предоставляются как входные данные начального корпуса с помощью f.Add.

Убедитесь, что новый пакет unicode/utf8 импортирован.

package main

import (
    "testing"
    "unicode/utf8"
)

Когда модульный тест преобразован в fuzz тест, пришло время снова запустить тест.

Запустите код

1. Запустите fuzz тест без фаззинга, чтобы убедиться, что исходные входные данные пройдены.

$ go test
PASS
ok      example/fuzz  0.013s

Вы также можете запустить go test -run=FuzzReverse, если у вас есть другие тесты в этом файле, и вы хотите запустить только fuzz тест.

2. Запустите FuzzReverse с фаззингом, чтобы увидеть, не приведут ли какие-либо случайно сгенерированные вводы строк к сбою. Это выполняется с помощью go test с новым флагом -fuzz.

$ go test -fuzz=Fuzz
fuzz: elapsed: 0s, gathering baseline coverage: 0/3 completed
fuzz: elapsed: 0s, gathering baseline coverage: 3/3 completed, now fuzzing with 8 workers
fuzz: minimizing 38-byte failing input file...
--- FAIL: FuzzReverse (0.01s)
    --- FAIL: FuzzReverse (0.00s)
        reverse_test.go:20: Reverse produced invalid UTF-8 string "\x9c\xdd"

    Failing input written to testdata/fuzz/FuzzReverse/af69258a12129d6cbba438df5d5f25ba0ec050461c116f777e77ea7c9a0d217a
    To re-run:
    go test -run=FuzzReverse/af69258a12129d6cbba438df5d5f25ba0ec050461c116f777e77ea7c9a0d217a
FAIL
exit status 1
FAIL    example/fuzz  0.030s

Во время фаззинга произошел сбой, и ввод, вызвавший проблему, записывается в файл начального корпуса (seed corpus), который будет запущен при следующем вызове go test, даже без флага -fuzz. Чтобы просмотреть ввод, вызвавший сбой, откройте файл корпуса, записанный в каталог testdata/fuzz/FuzzReverse, в текстовом редакторе. Ваш файл исходного корпуса может содержать другую строку, но формат будет таким же.

go test fuzz v1
string("泃")

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

3. Снова запустите go test без флага -fuzz; будет использоваться новая ошибочная запись корпуса исходных данных:

$ go test
--- FAIL: FuzzReverse (0.00s)
    --- FAIL: FuzzReverse/af69258a12129d6cbba438df5d5f25ba0ec050461c116f777e77ea7c9a0d217a (0.00s)
        reverse_test.go:20: Reverse produced invalid string
FAIL
exit status 1
FAIL    example/fuzz  0.016s

Поскольку наш тест провалился, пришло время отладки.

Исправить ошибку неверной строки

В этом разделе вы отладите сбой и исправите ошибку.

Диагностируйте ошибку

Есть несколько различных способов отладки этой ошибки. Если вы используете VS Code в качестве текстового редактора, вы можете настроить отладчик для исследования.

В этом руководстве мы будем регистрировать полезную информацию об отладке на вашем терминале.

Во-первых, рассмотрим документацию для utf8.ValidString.

ValidString сообщает, состоит ли s полностью из допустимых рун в кодировке UTF-8.

Текущая функция Reverse переворачивает строку байт за байтом, и в этом наша проблема. Чтобы сохранить руны исходной строки в кодировке UTF-8, мы должны вместо этого перевернуть строку руна за руной.

Чтобы выяснить, почему ввод (в данном случае китайский иероглиф 泃) приводит к тому, что Reverse создает недопустимую строку при переворачивании, вы можете проверить количество рун в перевернутой строке.

Напишите код

В текстовом редакторе замените цель fuzz в FuzzReverse следующим.

f.Fuzz(func(t *testing.T, orig string) {
    rev := Reverse(orig)
    doubleRev := Reverse(rev)
    t.Logf("Number of runes: orig=%d, rev=%d, doubleRev=%d", utf8.RuneCountInString(orig), utf8.RuneCountInString(rev), utf8.RuneCountInString(doubleRev))
    if orig != doubleRev {
        t.Errorf("Before: %q, after: %q", orig, doubleRev)
    }
    if utf8.ValidString(orig) && !utf8.ValidString(rev) {
        t.Errorf("Reverse produced invalid UTF-8 string %q", rev)
    }
})

Здесь строка t.Logf будет выводиться в командную строку в случае возникновения ошибки или при выполнении теста с ключом -v, что может помочь вам отладить эту конкретную проблему.

Запустите код

Запустите тест, используя go test

$ go test
--- FAIL: FuzzReverse (0.00s)
    --- FAIL: FuzzReverse/28f36ef487f23e6c7a81ebdaa9feffe2f2b02b4cddaa6252e87f69863046a5e0 (0.00s)
        reverse_test.go:16: Number of runes: orig=1, rev=3, doubleRev=1
        reverse_test.go:21: Reverse produced invalid UTF-8 string "\x83\xb3\xe6"
FAIL
exit status 1
FAIL    example/fuzz    0.598s

Во всем начальном корпусе (seed corpus) использовались строки, в которых каждый символ представлял собой один байт. Однако для таких символов, как 泃, может потребоваться несколько байтов. Таким образом, обращение строки байт за байтом сделает недействительными многобайтовые символы.

Примечание. Если вам интересно, как Go работает со строками, прочтите запись в блоге Строки, байты, руны и символы в Go для более глубокого понимания.

Лучше поняв ошибку, исправьте ошибку в функции Reverse.

Исправьте ошибку

Чтобы исправить функцию Reverse, пройдемся по строке по рунам, а не по байтам.

Напишите код

В текстовом редакторе замените существующую функцию Reverse() следующей.

func Reverse(s string) string {
    r := []rune(s)
    for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
        r[i], r[j] = r[j], r[i]
    }
    return string(r)
}

Ключевое отличие состоит в том, что Reverse теперь перебирает каждую руну в строке, а не каждый байт.

Запустите код

Запустите тест, используя go test

$ go test
PASS
ok      example/fuzz  0.016s

Теперь тест проходит!

Еще раз проверьте его с помощью go test -fuzz, чтобы увидеть, есть ли какие-либо новые ошибки.

$ go test -fuzz=Fuzz
fuzz: elapsed: 0s, gathering baseline coverage: 0/37 completed
fuzz: minimizing 506-byte failing input file...
fuzz: elapsed: 0s, gathering baseline coverage: 5/37 completed
--- FAIL: FuzzReverse (0.02s)
    --- FAIL: FuzzReverse (0.00s)
        reverse_test.go:33: Before: "\x91", after: "�"

    Failing input written to testdata/fuzz/FuzzReverse/1ffc28f7538e29d79fce69fef20ce5ea72648529a9ca10bea392bcff28cd015c
    To re-run:
    go test -run=FuzzReverse/1ffc28f7538e29d79fce69fef20ce5ea72648529a9ca10bea392bcff28cd015c
FAIL
exit status 1
FAIL    example/fuzz  0.032s

Мы можем видеть, что строка отличается от оригинала после того, как дважды была перевернута. На этот раз сам ввод является недопустимым юникодом. Как это возможно, если мы фаззим со строками?

Давайте снова отладим.

Исправить ошибку двойного реверса

В этом разделе вы отладите ошибку двойного реверса и исправите ее.

Диагностируйте ошибку

Как и раньше, есть несколько способов отладки этого сбоя. В этом случае использование отладчика было бы отличным подходом.

В этом руководстве мы будем регистрировать полезную информацию об отладке в функции Reverse.

Посмотрите внимательно на перевернутую строку, чтобы найти ошибку. В Go строка представляет собой срез байтов, доступный только для чтения, и может содержать байты, которые не являются допустимыми UTF-8. Исходная строка представляет собой срез байтов с одним байтом, '\x91'. Когда входная строка имеет значение []rune, Go кодирует байтовый срез в UTF-8 и заменяет байт символом UTF-8 �. Когда мы сравниваем замещающий символ UTF-8 с входным байтовым срезом, они явно не равны.

Напишите код

В текстовом редакторе замените функцию Reverse на следующую.

func Reverse(s string) string {
    fmt.Printf("input: %q\n", s)
    r := []rune(s)
    fmt.Printf("runes: %q\n", r)
    for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
        r[i], r[j] = r[j], r[i]
    }
    return string(r)
}

Это поможет нам понять, что происходит не так при преобразовании строки в срез рун.

Запустите код

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

$ go test -run=FuzzReverse/28f36ef487f23e6c7a81ebdaa9feffe2f2b02b4cddaa6252e87f69863046a5e0
input: "\x91"
runes: ['�']
input: "�"
runes: ['�']
--- FAIL: FuzzReverse (0.00s)
    --- FAIL: FuzzReverse/28f36ef487f23e6c7a81ebdaa9feffe2f2b02b4cddaa6252e87f69863046a5e0 (0.00s)
        reverse_test.go:16: Number of runes: orig=1, rev=1, doubleRev=1
        reverse_test.go:18: Before: "\x91", after: "�"
FAIL
exit status 1
FAIL    example/fuzz    0.145s

Чтобы запустить конкретную запись корпуса в FuzzXxx/testdata, вы можете указать {FuzzTestName}/{filename} для -run. Это может быть полезно при отладке.

Зная, что ввод является недопустимым юникодом, давайте исправим ошибку в нашей функции Reverse.

Исправьте ошибку

Чтобы решить эту проблему, давайте вернем ошибку, если ввод в Reverse недействителен в кодировке UTF-8.

Напишите код

В текстовом редакторе замените существующую функцию Reverse на следующую.

func Reverse(s string) (string, error) {
    if !utf8.ValidString(s) {
        return s, errors.New("input is not valid UTF-8")
    }
    r := []rune(s)
    for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
        r[i], r[j] = r[j], r[i]
    }
    return string(r), nil
}

Это изменение вернет ошибку, если входная строка содержит символы, недопустимые в кодировке UTF-8.

Поскольку функция Reverse теперь возвращает ошибку, измените main функцию, чтобы отбросить дополнительное значение ошибки. Замените существующую main функцию следующей.

func main() {
    input := "The quick brown fox jumped over the lazy dog"
    rev, revErr := Reverse(input)
    doubleRev, doubleRevErr := Reverse(rev)
    fmt.Printf("original: %q\n", input)
    fmt.Printf("reversed: %q, err: %v\n", rev, revErr)
    fmt.Printf("reversed again: %q, err: %v\n", doubleRev, doubleRevErr)
}

Эти вызовы Reverse должны возвращать нулевую ошибку, поскольку входная строка является допустимой UTF-8.

Вам нужно будет импортировать пакеты errors и unicode/utf8. Оператор импорта в main.go должен выглядеть следующим образом.

import (
    "errors"
    "fmt"
    "unicode/utf8"
)

Измените файл reverse_test.go, чтобы проверить наличие ошибок и пропустить тест, если ошибки генерируются при возврате.

func FuzzReverse(f *testing.F) {
    testcases := []string {"Hello, world", " ", "!12345"}
    for _, tc := range testcases {
        f.Add(tc)  //  Используйте f.Add для предоставления начального корпуса (seed corpus)
    }
    f.Fuzz(func(t *testing.T, orig string) {
        rev, err1 := Reverse(orig)
        if err1 != nil {
            return
        }
        doubleRev, err2 := Reverse(rev)
        if err2 != nil {
             return
        }
        if orig != doubleRev {
            t.Errorf("Before: %q, after: %q", orig, doubleRev)
        }
        if utf8.ValidString(orig) && !utf8.ValidString(rev) {
            t.Errorf("Reverse produced invalid UTF-8 string %q", rev)
        }
    })
}

Вместо возврата вы также можете вызвать t.Skip(), чтобы остановить выполнение этого fuzz ввода.

Запустите код

Запустите тест, используя go test

$ go test
PASS
ok      example/fuzz  0.019s

Фаззите с помощью go test -fuzz=Fuzz, затем через несколько секунд прекратите фаззинг, нажав Ctrl-C.

$ go test -fuzz=Fuzz
fuzz: elapsed: 0s, gathering baseline coverage: 0/38 completed
fuzz: elapsed: 0s, gathering baseline coverage: 38/38 completed, now fuzzing with 4 workers
fuzz: elapsed: 3s, execs: 86342 (28778/sec), new interesting: 2 (total: 35)
fuzz: elapsed: 6s, execs: 193490 (35714/sec), new interesting: 4 (total: 37)
fuzz: elapsed: 9s, execs: 304390 (36961/sec), new interesting: 4 (total: 37)
...
fuzz: elapsed: 3m45s, execs: 7246222 (32357/sec), new interesting: 8 (total: 41)
^Cfuzz: elapsed: 3m48s, execs: 7335316 (31648/sec), new interesting: 8 (total: 41)
PASS
ok      example/fuzz  228.000s

fuzz тест будет выполняться до тех пор, пока не обнаружит ошибочный ввод, если только вы не передадите флаг -fuzztime. По умолчанию выполняется вечно, если не происходит сбоев, и процесс можно прервать с помощью Ctrl-C.

Проведите фаззинг с помощью go test -fuzz=Fuzz -fuzztime 30s, который будет фаззить в течение 30 секунд перед выходом, если сбоев не обнаружено.

$ go test -fuzz=Fuzz -fuzztime 30s
fuzz: elapsed: 0s, gathering baseline coverage: 0/5 completed
fuzz: elapsed: 0s, gathering baseline coverage: 5/5 completed, now fuzzing with 4 workers
fuzz: elapsed: 3s, execs: 80290 (26763/sec), new interesting: 12 (total: 12)
fuzz: elapsed: 6s, execs: 210803 (43501/sec), new interesting: 14 (total: 14)
fuzz: elapsed: 9s, execs: 292882 (27360/sec), new interesting: 14 (total: 14)
fuzz: elapsed: 12s, execs: 371872 (26329/sec), new interesting: 14 (total: 14)
fuzz: elapsed: 15s, execs: 517169 (48433/sec), new interesting: 15 (total: 15)
fuzz: elapsed: 18s, execs: 663276 (48699/sec), new interesting: 15 (total: 15)
fuzz: elapsed: 21s, execs: 771698 (36143/sec), new interesting: 15 (total: 15)
fuzz: elapsed: 24s, execs: 924768 (50990/sec), new interesting: 16 (total: 16)
fuzz: elapsed: 27s, execs: 1082025 (52427/sec), new interesting: 17 (total: 17)
fuzz: elapsed: 30s, execs: 1172817 (30281/sec), new interesting: 17 (total: 17)
fuzz: elapsed: 31s, execs: 1172817 (0/sec), new interesting: 17 (total: 17)
PASS
ok      example/fuzz  31.025s

Фаззинг пройден!

Завершенный код — main.go —

package main

import (
    "errors"
    "fmt"
    "unicode/utf8"
)

func main() {
    input := "The quick brown fox jumped over the lazy dog"
    rev, revErr := Reverse(input)
    doubleRev, doubleRevErr := Reverse(rev)
    fmt.Printf("original: %q\n", input)
    fmt.Printf("reversed: %q, err: %v\n", rev, revErr)
    fmt.Printf("reversed again: %q, err: %v\n", doubleRev, doubleRevErr)
}

func Reverse(s string) (string, error) {
    if !utf8.ValidString(s) {
        return s, errors.New("input is not valid UTF-8")
    }
    r := []rune(s)
    for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
        r[i], r[j] = r[j], r[i]
    }
    return string(r), nil
}

— reverse_test.go —

package main

import (
    "testing"
    "unicode/utf8"
)

func FuzzReverse(f *testing.F) {
    testcases := []string{"Hello, world", " ", "!12345"}
    for _, tc := range testcases {
        f.Add(tc) // Use f.Add to provide a seed corpus
    }
    f.Fuzz(func(t *testing.T, orig string) {
        rev, err1 := Reverse(orig)
        if err1 != nil {
            return
        }
        doubleRev, err2 := Reverse(rev)
        if err2 != nil {
            return
        }
        if orig != doubleRev {
            t.Errorf("Before: %q, after: %q", orig, doubleRev)
        }
        if utf8.ValidString(orig) && !utf8.ValidString(rev) {
            t.Errorf("Reverse produced invalid UTF-8 string %q", rev)
        }
    })
}


Читайте также: