Golang Дайджест
8.23K subscribers
40 photos
1 file
189 links
Самое интересное из мира Go: новости, статьи, проекты, сервисы, изменения в языке и др.

Посты публикуются не часто - только самое важное, с чем я лично ознакомился.

Поэтому можно не мьютить канал =)

Обратная связь: @justskiv
Download Telegram
🕵️‍♂️ Как Cloudflare нашли баг в компиляторе Go

- Источник
- Перевод

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

Заваривайте кофе или чаёк и устраивайтесь поудобнее, мы начинаем.. ☕️

Начало истории:

🟠Cloudflare обрабатывает 84 миллиона HTTP-запросов в секунду через 330 дата-центров. При таком масштабе даже самые редкие баги проявляются регулярно.

Один из сервисов начал спорадически паниковать на arm64-машинах с ошибкой "traceback did not unwind completely" — признак повреждения стека. Решили, что это редкая проблема с памятью и не стали копать глубже.

Но паники продолжились.

Первая теория:

- Все критические паники происходили при раскрутке стека
- Коррелировали с recovered panic
- В коде было старое использование panic / recover для обработки ошибок
- Есть похожий баг на GitHub

Убрали panic / recover — паники прекратились. Вздохнули с облегчением.

Но через месяц паники вернулись:

Теперь до 30 в день. Без recovered panics вообще. Никакой корреляции с релизами, инфраструктурой, или положением Марса 🪐

🕵️‍♂️ Расследование:

Все крэши происходили в (*unwinder).next — при раскрутке стека. Два типа:
1. Явная критическая ошибка от среды выполнения
2. SIGSEGV при разыменовании указателя

Заметили паттерн: все segfault'ы происходили при асинхронном вытеснении функции NetlinkSocket.Receive из библиотеки go-netlink.

Что такое асинхронное вытеснение:

До Go 1.14 планировщик был кооперативным — горутины сами решали, когда отдать управление. С 1.14 появилось асинхронное вытеснение: если горутина работает больше 10ms, среда выполнения отправляет SIGURG и принудительно вызывает asyncPreempt.

➡️ Так, для понимания этих деталей коротким ликбезом не отделаться, но у меня есть подробнейшие ролик и статья на эту тему. После них будете кристаллически ясно понимать суть, касательно планировщика, обещаю.

Прорыв:

Удалось получить дамп ядра и посмотреть в отладчике. Горутина была остановлена между двумя инструкциями в эпилоге функции:

nl_linux.go:779 0x555577cb287c ADD $80, RSP, RSP
nl_linux.go:779 0x555577cb2880 ADD $(16<<12), RSP, RSP
nl_linux.go:779 0x555577cb2884 RET


Вытеснение произошло между двумя ADD, которые корректируют указатель стека. Стек оказался в несогласованном состоянии!

Почему две инструкции ADD:

На arm64 непосредственный операнд в инструкции ADD — это 12 бит. Для больших значений компилятор разбивает на две операции: ADD $x, RSP и ADD $(y<<12), RSP. Если прерывание происходит между ними, RSP указывает в середину стека, а не на его вершину.

Простейшее воспроизведение:

//go:noinline
func big_stack(val int) int {
var big_buffer = make([]byte, 1 << 16)
// ... работа с буфером
}

func main() {
go func() {
for { runtime.GC() }
}()
for { _ = big_stack(1000) }
}


Функция со стековым кадром >64KB, GC в цикле для раскрутки стека, и бесконечные вызовы. Через пару минут — segfault!

Суть бага:

1. Асинхронное вытеснение между ADD x, RSP и ADD (y<<12), RSP
2. GC запускает раскрутку стека
3. Раскрутчик пытается прочитать родительский кадр по невалидному RSP
4. Крэш

Условие гонки в одну инструкцию. Невероятно редкий баг! 💎

Исправление:

Для стеков >4KB компилятор теперь сначала строит смещение во временном регистре, потом делает одну атомарную операцию ADD. Вытеснение может произойти до или после, но никогда не во время.

Исправлено в go1.23.12, go1.24.6 и go1.25.0.

————

Очень люблю такие истории!
Месяцы расследования, работа с дампами ядра, изучение внутренностей среды выполнения и ассемблера. И в итоге — баг на уровне компилятора, который проявляется только при стечении обстоятельств: arm64 + большой стек + асинхронное вытеснение + GC + луна в правильной фазе

#compiler #debugging
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥409👍6