Tôi ghét trình biên dịch
(xeiaso.net)- Anubis đang thiết kế để mở rộng cơ chế proof-of-work bảo vệ website vượt ra ngoài SHA-256, đồng thời để client và server cùng chạy cùng một logic kiểm tra WebAssembly
- Để không loại trừ các môi trường tắt WebAssembly, dự án đã chuẩn bị đường biên dịch lại sang JavaScript, nhưng cách này chậm hơn WebAssembly và còn có thể chậm hơn nữa nếu JIT cũng bị tắt
- Bản
wasm2jstrong các bản phân phối Linux đã cũ nên cho đầu ra khác với bản Homebrew, vì vậy để có build tái lập được, dự án đã đóng gói kèmwasm2jsđược build bằngwasi-sdk - Với build C/C++,
__DATE__,__TIME__,wasm-opttrong$PATH, và thứ tự con trỏ trong mã xử lý ngoại lệ có thể làm đầu ra ở mức byte dao động dù đầu vào giống nhau - Bản triển khai cuối cùng dùng
--no-wasm-opt,setarch --addr-no-randomize, xác minh SHA-256 riêng cho x86_64 và arm64, cùng kiểm tra rebuild trong CI để đảm bảo tính quyết định trong cùng kiến trúc
Proof-of-work WebAssembly của Anubis và đường thay thế bằng JavaScript
- Anubis muốn thêm kiểm tra proof-of-work dựa trên WebAssembly để quản trị viên có thể dùng các cơ chế proof-of-work không phải SHA-256 để bảo vệ website
- Mục tiêu cốt lõi là không phải triển khai riêng logic kiểm tra cho client và server mà chỉ định nghĩa ở một nơi duy nhất
- Client và server cùng liên kết vào cùng một WebAssembly để chạy logic kiểm tra
- Hướng tới cấu trúc có thể xác nhận hai bên hoạt động theo lockstep
- Các client tắt WebAssembly cũng nằm trong phạm vi cân nhắc
- Có ràng buộc là không muốn trên thực tế loại người dùng khỏi website
- Anubis phải cân bằng giữa trải nghiệm người dùng, trải nghiệm quản trị viên và trải nghiệm nhà phát triển
- Giải pháp vòng được chọn là biên dịch lại WebAssembly sang JavaScript
- Lấy cảm hứng từ The Birth and Death of JavaScript
- JavaScript tạo ra chậm hơn WebAssembly tương đương
- Khi tắt WebAssembly, đôi khi JavaScript JIT cũng bị tắt theo nên có thể còn chậm hơn
- Việc nó có hiệu quả hơn JavaScript hiện tại trên phần cứng cấu hình thấp hay không vẫn cần nghiên cứu thêm
Vì sao phải đóng gói kèm wasm2js
- Công cụ cần thiết là
wasm2jscủa dự án binaryen wasm2jscó sẵn dưới dạng gói trong các bản phân phối Linux, nhưng phiên bản distro đã cũ nên không thể tạo ra đầu ra giống với bản Homebrew trong môi trường phát triển- Build tái lập được đòi hỏi tính quyết định của đầu ra
- Để người dùng và người đóng gói có thể tin cậy binary
wasm2jsđược commit vào kho Anubis, họ phải có khả năng tự build cùng phiên bản và nhận được cùng một dãy byte - Nếu có thể, ngay cả trên máy của người khác cũng nên cho ra cùng một dãy byte
- Để người dùng và người đóng gói có thể tin cậy binary
- Vì vậy dự án đưa vào một bản sao
wasm2jsđược build bằng wasi-sdk nhắm tới đích WebAssembly
Những điểm tính tái lập dễ bị phá vỡ trong build C/C++
- Ngay cả khi đưa vào cùng byte nguồn và cùng đầu vào, đầu ra của trình biên dịch không phải lúc nào cũng là cùng một dãy byte
- Trong C/C++, chỉ riêng các macro dựng sẵn như
__DATE__và__TIME__cũng có thể tạo ra đầu ra không quyết định- Ví dụ
hello.cppđược viết để in ra ngày và giờ tại thời điểm build - Một lần build in ra
Jun 18 2026 00:00:59, còn lần khác in raJun 18 2026 00:01:11 - Byte mã nguồn giống nhau nhưng đầu ra của trình biên dịch lại khác nhau
- Ví dụ
- Với trình biên dịch nhỏ thì về lý thuyết có thể quyết định, nhưng trình biên dịch thực tế có nhiều biến số phức tạp hơn
Vấn đề Clang âm thầm chạy wasm-opt trong $PATH
- Ngoài
wasm2js, binaryen còn cówasm-optđể tối ưu đầu ra của trình biên dịch WebAssembly - Clang gọi
wasm-optra shell trong quá trình build- Bình thường đây là hành vi hợp lý để cải thiện hiệu năng
- Nhưng trong trường hợp này, khác biệt phiên bản
wasm-opttrong$PATHđã phá vỡ tính tái lập
wasm-opttrên DGX Spark là/usr/bin/wasm-optphiên bản 108, cònwasm-optHomebrew trên workstation là phiên bản 130wasi-sdkvà binaryen phụ thuộc vào WebAssembly Exceptions extension- Theo Can I use, 93.86% người dùng trình duyệt đang dùng engine hỗ trợ tính năng này
- C++ là ngôn ngữ dùng ngoại lệ nhiều, nên xử lý ngoại lệ native của WebAssembly có thể giảm boilerplate
- wasmtime và wazero cần bật hỗ trợ ngoại lệ một cách tường minh
- Có thể truyền
-W exceptions=ycho wasmtime - Còn wazero cần một runner harness tùy biến
- Có thể truyền
wasm-optcũ trên máy arm đã thoát khi gặp lệnh xử lý ngoại lệ, làm build thất bại- Bằng cách truyền
--no-wasm-optở bước liên kết, dự án đã loại bỏ đường gây mất tính tái lập này
Tác động của bố trí địa chỉ tới việc sinh mã xử lý ngoại lệ
- Phiên bản Clang đang dùng cho thấy việc sinh mã nhạy với địa chỉ trong nhánh xử lý ngoại lệ của quá trình biên dịch
wasm2js - Giá trị con trỏ thô ảnh hưởng tới thứ tự đầu ra của một số khối
try_table- Mỗi lần build có thể chênh khoảng 29 byte
- Tính toán gần như giống nhau, nhưng thứ tự byte thay đổi và tham chiếu catch cũng thay đổi
- Ngay cả khi build cùng phiên bản cố định của
wasm2jstrên máy arm64, thứ tự lặp con trỏ vẫn khác với workstation nên cùng vấn đề lại xuất hiện - Có hai cách vòng
- Dùng
setarch --addr-no-randomizeđể tắt ngẫu nhiên hóa không gian địa chỉ cho lần build đó - Tạo checksum SHA-256 known-good riêng cho x86_64 và arm64 trên các máy đáng tin cậy
- Dùng
- CI chạy
./build.shtrong./utils/wasm/wasm2jsrồi xác minh checksum- Nếu khớp
shasums.x86_64thì được tính là vượt qua checksum x86_64 - Nếu khớp
shasums.arm64thì được tính là vượt qua checksum arm64 - Nếu không khớp cả hai, nó sẽ in ra SHA-256 của
wasm-opt_130.wasmvàwasm2js_130.wasmrồi thất bại
- Nếu khớp
- Tác vụ CI này chạy trên cả host x86_64 và arm64
- Tính tái lập trên toàn bộ các host vẫn chưa đạt được, và vấn đề đó hiện vẫn là một bug upstream của LLVM
- Ở trạng thái hiện tại, ít nhất bản build vẫn hoạt động một cách quyết định trong cùng kiến trúc
1 bình luận
Ý kiến trên Lobste.rs
Đây là lần đầu tôi biết
clanglén chạywasm-opttrong$PATH, và tôi thấy điều đó thật vô lýVì vậy tôi đã kiểm tra xem điều này có ảnh hưởng đến
zig cckhông, và may mắn là không, vì nó chỉ chạy khi dùngclanglàm trình điều khiển liên kếtNếu
clangquyết định thứ tự dựa vào cách bố trí địa chỉ thì cá nhân tôi xem đó là một lỗi, và nếu vẫn tái hiện được ở bản phát hành mới nhất thì chắc tôi sẽ báo như vậyNhững nỗ lực để loại bỏ kiểu vấn đề này đã kéo dài suốt nhiều năm
clang.exetrên Windows như một trình biên dịch chéo cho ổn định thì còn phát điên hơn nữaclang có khoảng 500 cách để giả định rằng nó sẽ được build cho hệ thống native
Không phải để chỉ trích, tôi tôn trọng việc đây là mã nguồn mở và OP đang cung cấp một dịch vụ phổ biến miễn phí
Nhưng tôi thực sự ghét việc web đang biến thành thế này. Giờ đây chuyện vào một website rồi thấy ngay trang tải Anubis lóe lên đã trở nên quá phổ biến, và tôi không biết liệu chúng ta có thực sự muốn một web nơi mọi trang nổi tiếng đều hiện màn hình splash proof-of-work hay không
Tôi cũng không biết còn lựa chọn nào khác khi crawler AI cứ tiếp tục kéo đến, nhưng tôi nghi ngờ liệu có bằng chứng nào cho thấy proof-of-work thực sự ngăn được crawler AI không. Chúng có nguồn vốn khổng lồ, và vốn dĩ đã bỏ ra lượng tính toán lớn hơn nhiều chỉ để đọc trang, nên chi phí giải proof-of-work có vẻ chỉ là một phần rất nhỏ
Trong thử nghiệm Anubis, nó rõ ràng là một biện pháp răn đe hiệu quả với lưu lượng không mong muốn, và chỉ với các quy tắc gần như mặc định đã liên tục chặn khoảng 90% yêu cầu của ba ứng dụng. DDR là 71.0%, ArcLight là 94.6%, Catalog là 92.4%
Ngày 30 tháng 5 lưu lượng bot tăng vọt, khiến Catalog gần như không thể phục vụ cho đến khi áp dụng Anubis vào ngày 3 tháng 6; ở đỉnh điểm ngày 1 tháng 6, con số tăng lên 3,4 triệu HTTP request từ 2,1 triệu IP duy nhất, và thời gian tải trang vượt quá 70 giây. Sau khi áp dụng Anubis vào ngày 4 tháng 6, dịch vụ lại phục vụ được người dùng; tổng số request ứng dụng xử lý là 125 nghìn và thời gian tải trang được cải thiện còn 2,12 giây
https://lobste.rs/s/ncyfcp/anubis_pilot_project_report_june_2025
Trong một trường hợp khác, vấn đề cũng được giải quyết ngay sau khi triển khai Anubis; họ có thể thấy chính xác thời điểm đó trên hệ thống giám sát, và sau đó không còn một cảnh báo nào nữa. Cuộc tấn công vẫn tiếp diễn nhưng tải máy chủ ở mức thấp nhất, và theo tôi Anubis không chỉ chặn AI scraper mà còn hoạt động như bảo vệ DDoS
https://lobste.rs/s/67ijih/day_anubis_saved_our_websites_from_ddos
https://orib.dev/tmp/bandwidth.png
Có người chỉ chặn được chúng bằng
meta refreshhoặc một nút phải bấm. Vì thế Anubis có tác dụng, nhưng điểm cốt lõi không phải là bản thân proof-of-work mà là hành vi ngoài dự kiếnNó còn khổ sở hơn cả thời dùng web bằng trình duyệt tắt JavaScript. Tôi chỉ muốn web đơn thuần là các tài liệu, nhưng giờ ở đâu cũng phải đi qua Cloudflare, Anubis và các cổng captcha
Rằng bot rồi sẽ luôn tìm được cách vượt WAF, còn người dùng thật thì lãng phí chu kỳ CPU ở màn hình tải
Đáng tiếc nhưng không có gì ngạc nhiên. Toolchain compiler có một lịch sử rất dài dựa vào những phụ thuộc ngầm vô lý kiểu “ngữ cảnh cục bộ phải tự nhiên mà đúng”
Tuy vậy LLVM lại là phía đã đi đầu trong việc loại bỏ các phụ thuộc như thế, nên thấy điều này ở clang có phần lạ lùng. Nhờ đó mà chẳng hạn compiler Rust có thể tồn tại mà không cần khái niệm trình biên dịch chéo riêng biệt
Nếu thử bootstrap một OS mà không dựa vào các build tool sẵn có thì điều đó hiện ra ngay. Việc tạo kernel, tạo libc và compiler cho kernel đó rồi chạy chúng, sau đó build lại toàn bộ trên OS mới là một công việc phi lý, cực kỳ phức tạp và nhạy cảm, đầy những giả định ngầm
Đây là kiểu vấn đề hiếm hoi chủ yếu chỉ chạm tới các nhà phát triển OS và compiler, nên gần như không có công cụ tốt hay thực tiễn tốt nhất nào; với mỗi cặp compiler+OS có lẽ chỉ khoảng 5 người trên thế giới thực sự hiểu toàn bộ mọi thứ
Tôi cũng nghĩ toolchain Zig lấy được một phần khả năng đó từ LLVM, dù tất nhiên tôi hiểu họ đã làm rất nhiều để tách bạch gọn gàng hơn. Giờ tôi còn tự hỏi liệu họ có còn dùng LLVM nữa không
Nhưng nếu clang cũng có cùng vấn đề, thì có lẽ LLVM đã không truyền lại một kiến trúc sạch sẽ hơn như tôi tưởng
Tôi biết bạn dùng Nix, nên tôi thắc mắc vì sao bạn không nhắc tới hoặc dùng Nix để giảm bớt ít nhất một phần biến động của môi trường
Ví dụ vấn đề
wasm-opttrong$PATHcó vẻ như đã có thể được giảm nhẹ bằng Nix, hay là bạn đã dùng rồi mà tôi bỏ sót?Ban đầu tôi ngây thơ nghĩ rằng việc chuyển wasm sang asm.js sẽ “dễ” thôi, nhưng hôm nay học được điều mới
Tiêu đề blog có vẻ câu click, nhưng nội dung thì hay
Tôi thực sự ghét kiểu câu click