5 điểm bởi GN⁺ 2025-01-24 | 4 bình luận | Chia sẻ qua WhatsApp
  • Trong lúc EdgeDB chuyển I/O mạng từ Python sang Rust, các bài kiểm thử HTTP fetch mới dựa trên reqwest tưởng như chỉ bị treo trên CI ARM64, nhưng thực tế là bị crash
  • Phân tích core dump cho thấy điểm lỗi không nằm ở mã HTTP mới mà ở bên trong getenv() của libc; nó thất bại khi duyệt mảng biến môi trường và cố đọc con trỏ sai 0x220
  • Một luồng khác đặt SSL_CERT_FILESSL_CERT_DIR trong đường đi của openssl-probe, khiến setenv() cấp phát lại environ; đồng thời đường xử lý lỗi của Python gọi getenv(), tạo ra race condition
  • Mã Rust không có unsafe rõ ràng, nhưng khi std::env::set_var() thay đổi môi trường toàn cục, khóa nội bộ của Rust không đồng bộ hóa được cả các runtime khác hay các lời gọi trực tiếp vào libc
  • Cách khắc phục là chuyển sang rustls trên Linux thay vì backend rust-native-tls/openssl của reqwest; Rust 2024 edition và glibc cũng đang thay đổi theo hướng giảm các vấn đề cùng loại

Crash chỉ lộ ra trên CI ARM64

  • EdgeDB đang xây dựng tính năng HTTP fetch mới trong quá trình port phần lớn mã I/O mạng từ Python sang Rust
  • Họ dùng reqwest làm thư viện HTTP client; tính năng này chạy qua ở máy cục bộ và runner CI x86_64, nhưng thỉnh thoảng thất bại trên runner CI ARM64
  • Ban đầu, có vẻ như test runner bị treo vô thời hạn; log CI không có lỗi và một bài kiểm thử cứ ở trạng thái đang chạy, rồi vài giờ sau bị timeout
  • Giả thuyết ban đầu là do khác biệt về memory model giữa Intel và ARM64
    • Intel có memory model tương đối chặt, trong đó mọi processor có một thứ tự toàn cục thống nhất đối với các lần ghi bộ nhớ
    • ARM có memory model với thứ tự yếu hơn, trong đó các lần ghi có thể xuất hiện theo thứ tự khác nhau đối với các luồng khác nhau

Truy vết core dump trong môi trường Docker CI

  • Máy CI chạy ban đêm trên Amazon AWS, nên có thể đăng nhập bằng người dùng root thực sự bên ngoài container để xem dmesg và log hệ thống
  • Họ tìm PID có vẻ đang bị treo cả trong lẫn ngoài container nhưng không thấy tiến trình đó, qua đó phát hiện đây không phải deadlock mà là crash
  • Vì Docker container là một process namespace, core dump được chuyển tới Docker host; trong journalctl, họ xác nhận core dump của tiến trình python3
  • Khi mở core bằng gdb lần đầu, backtrace không hữu ích vì thiếu các file .so bên trong container
  • Sau khi sao chép /lib, /usr, v.v. từ container và cấu hình solib-absolute-prefix của gdb, họ xác nhận điểm crash là getenv() trong libc.so.6

Con trỏ biến môi trường hỏng mà getenv() đã đọc

  • Backtrace đầy đủ có luồng getenv()__dcigettext()strerror_r()strerror()PyErr_SetFromErrnoWithFilenameObjects()
  • Không phải mã HTTP mới crash trực tiếp; Python đang tạo ngoại lệ dựa trên errno, đi qua đường liên quan tới gettext rồi gọi getenv()
  • Cài đặt getenv() của GLIBC 2.17 duyệt environ của POSIX như một danh sách char **, kiểm tra các con trỏ tới chuỗi biến môi trường cho đến con trỏ NULL cuối cùng
  • Dựa trên disassembly và trạng thái thanh ghi, getenv() crash khi cố đọc một byte tại địa chỉ x19 = 0x220
    • 0x220 rõ ràng không phải địa chỉ bộ nhớ hợp lệ
    • Khi kiểm tra chính environ, danh sách biến môi trường hiện tại trông nhất quán

Nguyên nhân: race condition giữa setenv()getenv()

  • setenv() không phải hàm có thể gọi an toàn trong môi trường đa luồng, và vấn đề gây crash lạ trong getenv() của libc đã nhiều lần được tái phát hiện
  • Khi đối chiếu disassembly với mã C, x20 tương ứng với con trỏ ep dùng để đi qua mảng environ
  • Tại thời điểm crash, x200x248b5000, còn environ hiện tại là 0x28655750, nằm sau đó khoảng 60MB
  • Khi so sánh vùng nhớ quanh mảng môi trường cũ với environ hiện tại, khác biệt cuối cùng xuất hiện ở các mục SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crtSSL_CERT_DIR=/etc/ssl/certs
  • Có thể xem đây là tình huống use-after-free: một luồng khác đã di chuyển environ trong lúc gọi setenv(), còn getenv() tiếp tục đọc mảng môi trường cũ

Mối liên hệ với openssl-probe và TLS backend

  • Từ một issue cũ liên quan đến rust-native-tls, họ tìm thấy manh mối rằng openssl-probe đặt các biến môi trường SSL_CERT_FILESSL_CERT_DIR để tìm chứng chỉ hệ thống
  • Trên Linux, đường đi này được gọi khi dùng backend openssl của rust-native-tls
  • Đoạn mã openssl-probe gây vấn đề đặt hai biến môi trường bằng env::set_var() mà không có unsafe rõ ràng
    • SSL_CERT_FILE
    • SSL_CERT_DIR
  • Vì sự kết hợp này, mã Rust không có unsafe đã tương tác xấu với việc sử dụng libc trong cùng tiến trình và gây crash

Vì sao tái hiện được trên Linux ARM64

  • Crash này xảy ra khi một luồng khác gọi getenv() đúng vào khoảnh khắc setenv() dùng realloc để di chuyển mảng môi trường
  • Để tái hiện, nhiều điều kiện phải trùng khớp cùng lúc
    • Số lượng biến môi trường phải vừa đủ để kích hoạt realloc
    • Một lỗi I/O không liên quan phải bị asyncio bắt được
    • Đường xử lý lỗi của Python phải gọi getenv() để lấy biến môi trường LANGUAGE đúng vào cùng thời điểm đó
  • Giá trị sai 0x220 gần với kích thước môi trường cũ tính theo word 64-bit
    • 0x220 / 8 = 68
    • Giá trị này đã ghi đè vị trí NULL kết thúc của khối môi trường cũ, và có vẻ là giá trị mà malloc hệ thống dùng để biểu thị kích thước free block
  • Vì cần rất nhiều tiền điều kiện, việc nó có thể tái hiện khá tốt trên một nền tảng duy nhất tự thân đã là may mắn

Manh mối xác nhận từ disassembly ARM64

  • Có lúc họ bối rối vì trong disassembly của getenv() không thấy rõ nơi x20 thay đổi
  • Điểm then chốt là chế độ định địa chỉ pre-index của AArch64
  • ldr x19, [x20, #8]! hoạt động như sau
    • x19 = *(x20 + 8)
    • x20 = x20 + 8
  • Do chế độ định địa chỉ này, x20 vẫn đang duyệt mảng con trỏ biến môi trường dù không được ghi tường minh ở vế trái

Bản sửa đã áp dụng và thay đổi ở các dự án liên quan

  • Cuối cùng, họ quyết định migrate sang rustls trên Linux thay vì backend rust-native-tls/openssl của reqwest
  • Lý do ban đầu chọn native TLS backend là để tránh phải mang theo hai TLS engine trong lúc chuyển mã Python sang Rust
  • Sau sự cố này, họ nhận định việc tạm thời mang theo hai TLS engine là chấp nhận được
  • Một phương án khác là thực hiện lời gọi đầu tiên tới try_init_ssl_cert_env_vars() trong lúc đang giữ GIL của Python
    • Rust có khóa nội bộ để ngăn race giữa các đoạn mã Rust khi đọc và ghi môi trường
    • Nhưng khóa đó không ngăn được trường hợp mã ở ngôn ngữ khác dùng trực tiếp libc
    • Giữ GIL ít nhất có thể ngăn race với các luồng Python
  • Dự án Rust đã nhận thức vấn đề này và có kế hoạch biến các hàm setter môi trường thành unsafe trong 2024 edition
  • Dự án glibc gần đây cũng đã thêm thay đổi nhằm tăng tính an toàn luồng của getenv() bằng cách tránh realloc và làm rò rỉ các mảng môi trường cũ

4 bình luận

 
carnoxen 2025-01-24

setenv không an toàn luồng, và C không muốn sửa điều đó

Hàm setenv lại gây rắc rối nữa rồi.

 
y15un 2025-01-24

Tôi sẽ đặt tiêu đề là "Tính không an toàn luồng của stdlib trong C thì ngay cả Rust vốn được xem là an toàn cũng không cứu nổi". :)

 
halfenif 2025-01-24

Tôi đã hiểu rõ ràng.

 
GN⁺ 2025-01-24
Ý kiến trên Hacker News
  • Điểm lớn nhất ở đây là trong edition tiếp theo của Rust, hàm thiết lập biến môi trường sẽ trở thành unsafe
    Nếu may mắn, ảnh hưởng có vẻ sẽ lan tới cả các crate gây ra những crash kiểu này; trong lúc đó, upstream đã có issue https://github.com/alexcrichton/openssl-probe/issues/30

    • Nhưng vấn đề gốc rễ, tức là không thể gọi an toàn getenvsetenv hoặc unsetenv từ các thread khác nhau, thì thực tế vẫn chưa được sửa
      Giải pháp đáng tin cậy có vẻ chỉ là thay đổi các hàm này để chúng bắt buộc phải giữ mutex
    • Nhận định của tác giả thư viện rằng “cách tốt nhất hiện nay cho vấn đề này là dùng thư viện như rustls và ngừng dùng crate này” là hợp lý, nên thật đáng mừng
      Đáng tiếc là hệ sinh thái lại không như vậy: https://github.com/seanmonstar/reqwest/blob/master/Cargo.tom...
    • Mọi người đã được “huấn luyện” để bỏ qua những block và tiền tố như ____UNSAFE_payattention__nevermindthatthisappears50timesinthisfile___
      Trong các web framework cũng tương tự: Vue có directive v-html, còn React có dangerouslySetInnerHTML; ở khía cạnh này, tôi cho rằng Vue rõ ràng tốt hơn
  • set_varremove_var trong thư viện chuẩn Rust sẽ chính thức yêu cầu block unsafe {} trong edition tiếp theo, edition 2024
    Tài liệu giờ cũng đã nhắc tới vấn đề an toàn, nhưng ngay từ đầu việc biến các hàm này thành safe là một sai lầm, và các ngôn ngữ cấp cao hơn cũng đã mắc sai lầm đó
    https://doc.rust-lang.org/stable/std/env/fn.set_var.html
    glibc có một bản vá giúp getenv an toàn hơn trong nhiều trường hợp môi trường bị sửa đổi, nhưng trong C vẫn có thể truy cập trực tiếp environ, nên khi có sửa đổi thì không thể hoàn toàn an toàn: https://github.com/bminor/glibc/commit/7a61e7f557a97ab597d6f...

    • Thật ngạc nhiên khi glibc giờ giữ lại các phiên bản cũ và áp dụng chính sách thay đổi kích thước theo hàm mũ
      Với mỗi biến môi trường đang hoạt động sẽ có rò rỉ bộ nhớ kích thước hằng số theo nghĩa khấu hao, nhưng bản thân các biến vốn đã có kiểu rò rỉ như vậy, thậm chí còn phụ thuộc vào độ dài và bao gồm cả các giá trị không còn được dùng nữa
      Chắc chắn có vẻ sẽ tồn tại những trường hợp sử dụng bệnh lý khiến bộ nhớ tăng vô hạn ngay cả trong các chương trình đúng theo API
      Việc đưa lỗi tăng bộ nhớ vô hạn vào các chương trình tuân thủ API để sửa những chương trình đã phá quy tắc bằng cách dùng API trên nhiều thread là điều thú vị nhưng cũng hơi khó chịu. Cảm giác như thực dụng hơn giáo điều
    • Nếu phần triển khai thư viện chuẩn có thể xử lý đồng bộ hóa, thì không rõ vì sao lại phải yêu cầu unsafe
  • Dù các maintainer thư viện chuẩn C phản đối việc làm cho setenv an toàn trong môi trường đa luồng, thì ít nhất cũng cần định nghĩa một API mới an toàn với thread, dù là trong POSIX hay trước hết tạo một chuẩn trên thực tế rồi để POSIX chấp nhận sau
    Nếu thời gian dùng để giải thích vì sao không thể làm gì được được dùng để sửa vấn đề này, thì hẳn đã có thể thay thế setenv cũ và loại bỏ/xóa nó khỏi nhiều dự án phần mềm
    Việc glibc đang thực hiện thay đổi nhằm gần như loại bỏ vấn đề này khiến lời của maintainer Musl rằng trong Musl không thể sửa được trở nên kém thuyết phục

    • Vấn đề lớn nhất không phải là không có API an toàn với thread, mà là sự tồn tại của extern char **environ;
      Chừng nào environ còn có thể truy cập công khai, thì thậm chí không có gì bảo đảm setenvgetenv sẽ được dùng, vì hai hàm này không bắt buộc
      Nếu có thể loại bỏ environ, việc làm cho setenvgetenv an toàn với thread sẽ khá đơn giản. Nếu không thể loại bỏ thì là bất khả thi, nhưng vẫn có thể lập luận rằng việc làm cho setenvgetenv an toàn với thread là một cải thiện, dù không phải giải pháp hoàn chỉnh
    • Có vẻ mọi hàm exec() không nhận môi trường làm đối số hoặc tìm kiếm PATH để tìm file thực thi cũng sẽ cần khóa
    • Tôi không thấy thuyết phục rằng mình biết rõ hơn các chuyên gia đã kết luận rằng không thể sửa theo cách tương thích ngược
  • Trên Linux, gặp lỗi liên quan đến biến môi trường có vẻ như là một kiểu nghi thức nhập môn, còn trên các Unix khác thì kỳ lạ là thường ít thành vấn đề hơn
    Linus và kernel sửa các lỗi POSIX theo hướng thực dụng, làm sao để chúng không thực sự phát nổ, nhưng khá buồn cười là glibc vẫn tụt hậu dù đã nhiều thập kỷ kể từ khi người ta cố gắng giảm nhẹ vấn đề dù chỉ một chút
    Đúng là có đủ thứ rắc rối như TZ, nhưng chỉ cần cung cấp getenv_r(), đồng bộ hóa nó với setenv(), và khi dùng getenv() thì cảnh báo ở giai đoạn biên dịch/liên kết thôi, thì phần lớn vấn đề đã biến mất rồi
    Xa hơn nữa, thậm chí có thể dùng cách copy-on-write (COW), để con trỏ môi trường ở chế độ chỉ đọc
    Thay vào đó, vấn đề bị đẩy sang từng ứng dụng riêng lẻ, mà tác giả ứng dụng thì gần như không thể biết các dependency đang làm gì, nên đây là một sai lầm lớn. Tình huống tôi từng gặp từ lâu cũng như vậy, và khi đó nhà cung cấp thư viện mã nguồn đóng đã bảo tôi đừng dùng cái bản sao Unix đồ chơi tên Linux đó nữa

    • Không rõ nhận định “trên Linux có lỗi liên quan đến biến môi trường, còn các Unix khác thì ít vấn đề hơn” xuất phát từ đâu
      Vấn đề không nằm ở phần hiện thực, mà là chính API. setenv(), unsetenv(), putenv(), đặc biệt là environ, về bản chất không an toàn trong chương trình đa luồng
      getenv_r() cũng không thể cứu hoàn toàn. Vì trong lúc một luồng đang sao chép giá trị cũ của biến môi trường vào buffer được cung cấp, một luồng khác vẫn có thể gọi setenv()
      Tất nhiên, trường hợp sau khi nhận từ getenv() rồi luồng khác gọi setenv() làm vùng nhớ đó mất hiệu lực thì getenv_r() khắc phục được, nhưng không có cách nào ngăn các lời gọi khác phá vỡ API
      libc có thể giảm nhẹ một phần vấn đề bằng cách giữ mutex bên trong getenv()/setenv()/putenv()/unsetenv(), nhưng libc vẫn không có cách nào bảo đảm giá trị mà getenv() trả về còn hợp lệ đủ lâu để mã gọi sử dụng
      Cũng không có cách hay nào để làm cho việc truy cập trực tiếp vào environ trở nên an toàn. Có thể biến environ thành thread-local, nhưng khi đó góc nhìn về môi trường của từng luồng có thể lệch nhau vĩnh viễn, và kết quả gọi getenv_r() có thể khác với việc kiểm tra trực tiếp environ
      Ở đây thật sự rất khó giữ tính tương thích ngược, và chỉ riêng việc thêm mutex để bảo vệ các hàm cũng có thể làm thay đổi ngữ nghĩa của chương trình hiện có và khiến chúng hỏng
  • Trước đây từng có bài viết nói rằng setenv rất tệ: https://www.evanjones.ca/setenv-is-not-thread-safe.html
    Cũng đã có thảo luận, và ngay từ bình luận đầu tiên đã nói nó gây vấn đề trong Rust: https://news.ycombinator.com/item?id=38342642

    • Đó là chuyện đã biết rồi
      Phần lớn các vấn đề còn lại ở đây có vẻ là do môi trường phát triển. Họ dùng Docker để kiểm thử trên một máy từ xa trong trung tâm dữ liệu của Amazon, và máy đó không báo được crash của tiến trình
      Hơn nữa, trong container cũng không có đủ thông tin debug symbol để lấy backtrace. Nếu ngay lần lỗi đầu tiên nhận được một backtrace sạch thì đã rõ ngay rồi
      Ngay từ đầu cũng không hiểu tại sao lại dùng setenv
  • Câu chuyện này làm tôi nhớ đến phong trào 12-factor app mà vài đồng nghiệp cũ từng rất tin tưởng. Một trong các “factor” đó là cấu hình ứng dụng phải được thực hiện bằng biến môi trường
    Tôi luôn thấy điều đó hơi ngớ ngẩn, vì cách cấu hình này giống như bỏ các giá trị kiểu chuỗi vào một cái giỏ trong một không gian tên phẳng
    Tôi cũng xem rủi ro của getenv()/setenv()/environ là một lý do mạnh mẽ để không dùng biến môi trường cho cấu hình
    Tất nhiên không phải lúc nào cũng có lựa chọn thay thế tuyệt vời và được hỗ trợ tốt. Tôi thích tệp cấu hình hơn, và cũng có thể dùng cấu hình mẫu rồi chỉ điền các giá trị cho dev, staging, production. Thường thì tôi dùng YAML dù nó có nhược điểm và cạm bẫy; có thể có định dạng tệp cấu hình tốt hơn, nhưng tôi vẫn cho rằng YAML tốt hơn biến môi trường rất nhiều

    • Có nhiều ác cảm mạnh với Windows và Microsoft, nhưng theo thời gian, khá nhiều lần thiết kế API của họ được chứng minh là hợp lý
      Trên NT, biến môi trường có thể được gõ kiểu và tạo mẫu, và còn có Registry, một cơ sở dữ liệu cấu hình có không gian tên. Dù nó dài dòng và kỳ quặc
      Hơn nữa, MSVC cung cấp phiên bản an toàn luồng cho gần như mọi hàm thư viện chuẩn
      Tôi thường nghe các lập trình viên C/C++ mới than phiền rằng MSVC thiếu khả năng tương thích POSIX, nhưng có vẻ họ không suy nghĩ sâu xem điều đó thật sự nghĩa là gì. Nó gần với mong muốn tương thích chéo với các chương trình C viết từ thập niên 1990 hơn
    • Tôi cũng có lo ngại tương tự về biến môi trường. Tôi không thích việc có thể đọc chúng ở bất kỳ đâu
      Nó cản trở khả năng suy luận hành vi chỉ từ chữ ký hàm, và biến nhiều hàm lẽ ra có thể là hàm thuần thành hàm không thuần
      Nếu có một tính năng ngôn ngữ cho phép đánh dấu ứng dụng sao cho trong mọi tiến trình, biến môi trường không thể được dùng và chỉ có thể được đọc một lần duy nhất theo lô, chứ không phải từng biến một, thì tôi nghĩ mình sẽ dùng nó ở mọi nơi
    • Bản thân getenv() hoàn toàn ổn, vấn đề là setenv()
      Về lý thuyết, môi trường được thiết lập trước khi ứng dụng bí ẩn đó khởi động, nên không cần dùng thứ này
      Nhưng một không gian tên phẳng, giá trị chuỗi, và một vùng toàn cục tự do dùng chung cho tất cả mọi người trong khi bạn không biết thư viện hay mô-đun nào sẽ được nạp vào, thì ngay cả khi không có vấn đề an toàn của setenv(), đó vẫn không phải ý hay
    • “12-factor app” nên có một phụ lục nói rằng trong lúc tiến trình còn sống, môi trường phải được xem là chỉ đọc
      Phần lớn các vấn đề mọi người nói ở đây dường như đến từ việc muốn lạm dụng môi trường như một kho khóa-giá trị cho trạng thái toàn cục có thể thay đổi. Tôi không hiểu vì sao lại muốn làm vậy
      JVM về cơ bản xem môi trường là bất biến, và có thể các công ty dùng Scala·Java như SoundCloud đã có ảnh hưởng đến phong trào 12-factor app. Tôi chưa từng gặp trường hợp môi trường thay đổi hay gây vấn đề luồng
      Ngay cả khi môi trường thay đổi, bản sao bất biến được tạo khi JVM khởi động vẫn giữ nguyên, và mã tương tác với môi trường qua API Java thông thường sẽ không thấy sửa đổi đó
      Vấn đề của tệp cấu hình là việc phân tích cú pháp diễn ra theo từng tiến trình. Vì vậy Linux/Unix mới lộn xộn như vậy. Mỗi công cụ có quy ước và cơ chế cấu hình khác nhau, không có chuẩn
      Trong hệ sinh thái Docker, bên trong container muốn làm gì thì làm, còn giao diện với bên ngoài là hoặc mount volume rồi tuân theo cách cấu hình phức tạp của từng ứng dụng, hoặc chỉ dùng biến môi trường
      Phần lớn phần mềm hiện đại chạy bằng Docker ngày nay đủ thân thiện với Docker để hành vi có thể được điều khiển hoàn toàn bằng môi trường, và trong nhiều trường hợp như vậy là đủ
      Nếu dùng Docker Compose hay Kubernetes, bạn sẽ có danh sách biến môi trường định nghĩa cách khởi động tiến trình trong một tệp YAML, nên ở mức nào đó cũng có được cấu trúc mong muốn. Tôi không thích YAML, nhưng nó hoạt động đủ tốt; vấn đề cú pháp rất có thể làm hỏng cả ngày của bạn, nhưng các lựa chọn thay thế cũng không phải không có vấn đề
    • Đây thật ra là một vấn đề riêng. Nếu đọc biến môi trường làm cấu hình rồi sau đó không đụng đến nữa thì hoàn toàn an toàn
      Tôi cũng dùng phong cách 12-factor app, nhưng sau khi vào ứng dụng thì kiểm tra hợp lệ và lưu trữ biến môi trường cùng dữ liệu. Sau đó thì không có vấn đề gì
  • Đây là một bài viết rất hay đào sâu vào một lỗi khó thấy
    Nó có đủ cả lỗi gián đoạn, đặc thù kiến trúc, ẩn trong phụ thuộc, Rust, Python GIL, cho đến gettext
    Những báo cáo khắc phục sự cố chi tiết như thế này là thứ gần nhất với việc tự mình trải qua. Trong tình huống “làm sao tôi biết được khi phụ thuộc của mình đang dùng nó”, rất khó nói đơn giản rằng “đừng dùng X là xong”

  • Họ nói “máy CI chạy ban đêm trên Amazon AWS, và có lợi thế là có thể dùng người dùng root thật chứ không phải container”, đồng thời lại nói “bên ngoài container không có các tệp cần thiết, và container được tối giản rất nhiều nên không thể dễ dàng cài gdb
    Giờ mọi người đã mất khả năng build và debug cục bộ mà không có đám mây và container rồi sao?

    • Đúng vậy. Thật sốc khi SaaS đám mây đã bóp méo hiểu biết của mọi người đến mức nào
      Để làm những việc hết sức nhỏ nhặt giờ lại cần đủ loại độ phức tạp đám mây và tầng triển khai. Chẳng khác nào đảo ngược 100% cuộc cách mạng PC để quay về thời điện toán mainframe cồng kềnh và đắt đỏ
      Lý do là vì tiền nằm ở đám mây, và đám mây là DRM. Đưa phần mềm lên đó thì có thể thu phí thuê bao, người dùng không né được, và sự phụ thuộc hoàn hảo được duy trì mãi mãi. Nhiều khi người dùng còn không thể lấy dữ liệu của chính mình ra
      Việc phân tích thời gian thực để tối ưu sản phẩm cũng tiện hơn
      Kiến trúc điện toán nằm ở hạ nguồn của mô hình kinh doanh. Mainframe ban đầu chết đi vì không có internet và PC rẻ hơn, nhưng cũng vì nhà cung cấp đã mất nhiều quyền lực khóa trói
      Giờ đã có cách hồi sinh một mô hình sinh lợi hơn nhiều. Quyền tự do phiền toái của người dùng đã biến mất, và nói thật, nếu người dùng có những quyền tự do đó thì họ thường không trả tiền, khiến việc kinh doanh phần mềm chất lượng cao khó thành lập
    • Đây là kiểu vấn đề như hỏng bộ nhớ ngẫu nhiên chỉ nổ trên ARM
      Rất có khả năng họ không tái hiện được crash trên máy cục bộ. Máy của lập trình viên phần lớn là x86 và ở đó có lẽ không bị crash
      Việc xử lý crash đáng ra phải tốt hơn, nhưng có vẻ họ cũng nhận thức được vấn đề đó và đó không phải trọng tâm được bàn ở đây
    • Tất nhiên không phải là đã mất khả năng, nhưng trên máy của chúng tôi thì không bị crash
    • Nhiều khả năng bạn không có thiết bị chạy kiến trúc gặp lỗi, vậy debug cục bộ bằng cách nào? Dù sao thì debug trong môi trường thật nơi lỗi xảy ra cũng nhanh hơn nhiều
  • Trạng thái toàn cục có thể thay đổi là xấu xa. Nếu là bạn bè thì đừng để bạn mình dùng trạng thái toàn cục có thể thay đổi
    Tôi ghét biến môi trường. Đây là “cách của Linux”, nhưng hãy tránh nó như tránh dịch bệnh. Rất khuyến nghị
    libc thật kinh khủng, và thế giới giờ nên vượt qua nó

    • Biến môi trường thì ổn nếu trong tiến trình coi nó là chỉ đọc
    • Nếu “trạng thái toàn cục có thể thay đổi là xấu xa” thì cũng phải vứt cả CPU và RAM đi
    • Tò mò không biết bạn đề xuất gì làm phương án thay thế
      Vấn đề không phải Linux, cũng không phải trạng thái hay tài nguyên toàn cục có thể thay đổi, cũng không phải libc
      Vấn đề là ở chỗ tại nơi làm việc, người ta không được cho thời gian để làm việc cho đúng. Chẳng hạn, muốn bắt lỗi bằng GDB trước khi sự cố xảy ra, sếp phải cho thời gian để kiên trì debug và truy ngược mã cùng mọi thứ mà mã đó động tới
      Có quá nhiều tiền đổ vào thứ mã còn nửa vời. Buồn nhưng đúng
    • libc đã đẩy thế giới vào thời đại thông tin
    • Tò mò không biết phương án thay thế bạn thích là gì
  • Vì có quá nhiều vấn đề kiểu này nên cuối cùng tôi đã vá getenv / setenv / putenv bằng LD_PRELOAD

    • Có phải làm bằng bản triển khai cố định làm rò rỉ môi trường giống như cái vừa được đưa vào glibc không?