5 पॉइंट द्वारा GN⁺ 2025-01-24 | 4 टिप्पणियां | WhatsApp पर शेयर करें
  • EdgeDB जब network I/O को Python से Rust में शिफ्ट कर रहा था, तब reqwest पर आधारित नए HTTP fetch टेस्ट केवल ARM64 CI पर अटके हुए लग रहे थे, लेकिन असल में वे crash हो रहे थे
  • core dump analysis से पता चला कि समस्या नई HTTP code में नहीं, बल्कि libc के getenv() के अंदर थी; वह environment variable array में चलते हुए गलत pointer 0x220 पढ़ने की कोशिश में fail हुआ
  • एक दूसरे thread ने openssl-probe path में SSL_CERT_FILE और SSL_CERT_DIR set करते समय setenv() के जरिए environ को reallocate कर दिया, और उसी समय Python error-handling path ने getenv() call किया, जिससे race condition बनी
  • Rust code में explicit unsafe नहीं था, लेकिन std::env::set_var() जब global environment बदल रहा था, तब दूसरे runtime या direct libc calls तक synchronization Rust के internal lock से नहीं हो रहा था
  • समाधान Linux पर reqwest के rust-native-tls/openssl backend की जगह rustls पर जाना था, और Rust 2024 edition तथा glibc भी इस तरह की समस्याओं को कम करने की दिशा में बदल रहे हैं

केवल ARM64 CI पर सामने आया crash

  • EdgeDB network I/O code के बड़े हिस्से को Python से Rust में port करते हुए नया HTTP fetch feature बना रहा था
  • HTTP client library के रूप में reqwest इस्तेमाल किया गया, और feature local व x86_64 CI runners पर pass हुआ, लेकिन ARM64 CI runner पर कभी-कभी fail होने लगा
  • शुरुआत में लगा कि test runner अनिश्चित समय तक hang हो गया है, और CI logs में बिना error के एक test लगातार running state में रह गया और कुछ घंटों बाद timeout हुआ
  • शुरुआती hypothesis Intel और ARM64 के memory model differences को लेकर था
    • Intel का memory model अपेक्षाकृत strict होता है, और memory writes के लिए सभी processors के बीच एक total order पर सहमति होती है
    • ARM का memory model weaker ordering वाला है, और writes अलग-अलग threads को अलग order में दिख सकती हैं

Docker CI environment में core dump track करना

  • nightly CI machine Amazon AWS पर चलती थी, इसलिए container के बाहर असली root user के रूप में connect करके dmesg और system logs देखे जा सकते थे
  • container के अंदर और बाहर उस PID को खोजा गया जो hang हुआ लगा था, लेकिन वह process मौजूद नहीं था; इससे साफ हुआ कि यह deadlock नहीं, बल्कि crash था
  • Docker container process namespace होता है, इसलिए core dump Docker host को forward हुआ, और journalctl में python3 process का core dump मिला
  • पहली बार gdb से core खोलने पर container के अंदर की .so files न होने के कारण backtrace बेकार था
  • container से /lib, /usr आदि copy करके और gdb का solib-absolute-prefix set करने के बाद पुष्टि हुई कि crash point libc.so.6 का getenv() था

getenv() द्वारा पढ़ा गया टूटा हुआ environment variable pointer

  • पूरा backtrace getenv()__dcigettext()strerror_r()strerror()PyErr_SetFromErrnoWithFilenameObjects() flow था
  • नया HTTP code सीधे crash नहीं हुआ था; Python errno-based exception बनाते समय gettext-related path से होकर getenv() call कर रहा था
  • GLIBC 2.17 में getenv() implementation POSIX के environ को char ** list के रूप में traverse करता है, और आखिरी NULL pointer तक environment variable string pointers check करता है
  • disassembly और register state के हिसाब से getenv() ने x19 = 0x220 address से byte पढ़ने की कोशिश की और crash हो गया
    • 0x220 स्पष्ट रूप से valid memory address नहीं था
    • environ itself को inspect करने पर current environment variable list consistent दिख रही थी

कारण: setenv() और getenv() की race condition

  • setenv() multi-threaded environment में safe तरीके से call की जा सकने वाली function नहीं है, और libc के getenv() में अजीब crashes पैदा करने वाली यह समस्या कई बार फिर से खोजी जा चुकी है
  • disassembly और C code की तुलना करने पर, x20 उस pointer ep से मेल खाता था जो environ array पर चलता है
  • crash के समय x20 0x248b5000 था, जबकि current environ 0x28655750 था, यानी करीब 60MB आगे
  • पुराने environment array के आसपास की memory और current environ की तुलना करने पर आखिरी अंतर SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt और SSL_CERT_DIR=/etc/ssl/certs entries में दिखा
  • लगता था कि एक दूसरा thread setenv() call के दौरान environ को move कर चुका था, और getenv() पुराने environment array को पढ़ता रह गया, यानी use-after-free स्थिति बन गई

openssl-probe और TLS backend connection

  • rust-native-tls से जुड़े एक पुराने issue में clue मिला कि openssl-probe system certificates खोजने के लिए SSL_CERT_FILE और SSL_CERT_DIR environment variables set करता है
  • Linux पर rust-native-tls का openssl backend इस्तेमाल करने पर वही path call होता है
  • problematic openssl-probe code explicit unsafe के बिना env::set_var() से दो environment variables set करता है
    • SSL_CERT_FILE
    • SSL_CERT_DIR
  • इस combination की वजह से बिना unsafe वाला Rust code उसी process के अंदर libc usage के साथ खराब interaction करके crash पैदा कर बैठा

ARM64 Linux पर reproduce होने की वजह

  • यह crash तभी होता है जब setenv() द्वारा realloc से environment array move किए जाने के उसी क्षण कोई दूसरा thread getenv() call करे
  • reproduce करने के लिए कई conditions एक साथ match होनी थीं
    • environment variables की संख्या इतनी होनी चाहिए कि realloc trigger हो
    • unrelated I/O failure asyncio में पकड़ा जाना चाहिए
    • Python error-handling path को LANGUAGE environment variable पाने के लिए ठीक उसी समय getenv() call करना चाहिए
  • गलत value 0x220 64-bit word के हिसाब से पुराने environment size के करीब थी
    • 0x220 / 8 = 68
    • पुराने environment block के terminating NULL की जगह यह value overwrite हुई थी, जो system malloc द्वारा free block size दिखाने के लिए इस्तेमाल की गई value लग रही थी
  • इतनी सारी preconditions चाहिए थीं, इसलिए किसी एक platform पर इसका काफी reproducible होना अपने-आप में किस्मत की बात थी

ARM64 disassembly में मिले संकेत

  • getenv() disassembly में x20 बदलने वाली जगह साफ नहीं दिख रही थी, जिससे confusion हुआ
  • key point AArch64 का pre-index addressing mode था
  • ldr x19, [x20, #8]! इस तरह काम करता है
    • x19 = *(x20 + 8)
    • x20 = x20 + 8
  • इस addressing mode की वजह से x20 explicit रूप से left side पर लिखे बिना भी environment variable pointer array traverse कर रहा था

लागू किया गया fix और related project changes

  • अंत में Linux पर reqwest के rust-native-tls/openssl backend की जगह rustls पर migrate करने का फैसला किया गया
  • मूल रूप से native TLS backend चुनने की वजह यह थी कि Python code को Rust में ले जाते समय दो TLS engines साथ में load करने से बचा जाए
  • इस समस्या के बाद short term में दो TLS engines load करना acceptable माना गया
  • एक alternative यह भी था कि try_init_ssl_cert_env_vars() का पहला call Python की GIL hold करते हुए किया जाए
    • Rust में Rust code के बीच environment पढ़ने-लिखने की race रोकने के लिए internal lock है
    • लेकिन यह उन cases को नहीं रोकता जहां दूसरी language का code सीधे libc इस्तेमाल करता है
    • GIL hold करने से कम से कम Python threads के साथ race रोकी जा सकती है
  • Rust project इस समस्या को पहले ही पहचान चुका था, और 2024 edition में environment setter functions को unsafe बनाने की योजना है
  • glibc project ने भी हाल में realloc से बचकर पुराने environment arrays को leak करने के तरीके से getenv() की thread safety बढ़ाने वाला change जोड़ा है

4 टिप्पणियां

 
carnoxen 2025-01-24

Setenv thread-safe नहीं है, और C इसे ठीक नहीं करना चाहता

Setenv फ़ंक्शन फिर से परेशानी खड़ी कर रहा है।

 
y15un 2025-01-24

मैं शीर्षक इस तरह लिखूँगा: 'C stdlib की thread-unsafety को वह Rust भी नहीं बचा सकती जिसे सुरक्षित कहा जाता है' :)

 
halfenif 2025-01-24

मैंने इसे निश्चित रूप से समझ लिया है।

 
GN⁺ 2025-01-24
Hacker News की रायें
  • यहाँ सबसे बड़ा पॉइंट यह है कि Rust के अगले edition में environment variable सेट करने वाले functions unsafe हो जाएंगे
    अगर किस्मत अच्छी रही, तो इसका असर उन crates तक भी नीचे जाएगा जो ऐसे crashes पैदा करते हैं, और इस बीच upstream में https://github.com/alexcrichton/openssl-probe/issues/30 issue दर्ज किया गया है

    • लेकिन मूल समस्या—यानी getenv और setenv या unsetenv को अलग-अलग threads से सुरक्षित रूप से call नहीं किया जा सकता—वास्तव में ठीक नहीं होती
      भरोसेमंद समाधान बस यही दिखता है कि इन functions को अनिवार्य रूप से mutex लेना पड़े, ऐसा बदला जाए
    • “आजकल इस समस्या का सबसे अच्छा हल rustls जैसी library इस्तेमाल करना और इस crate का इस्तेमाल बंद करना है” — library author का यह निष्कर्ष तार्किक है, यह देखकर अच्छा लगा
      दुर्भाग्य से ecosystem ऐसा नहीं है: https://github.com/seanmonstar/reqwest/blob/master/Cargo.tom...
    • लोग ____UNSAFE_payattention__nevermindthatthisappears50timesinthisfile___ जैसे blocks और prefixes को ignore करने के लिए trained हो जाते हैं
      web frameworks में भी कुछ ऐसा ही है: Vue में v-html directive है और React में dangerouslySetInnerHTML; इस मामले में मुझे लगता है कि Vue साफ़ तौर पर बेहतर है
  • Rust standard library के set_var और remove_var अगले edition, यानी 2024 edition, में ठीक से unsafe {} block मांगेंगे
    docs अब safety issue का ज़िक्र भी करते हैं, लेकिन शुरुआत में इन functions को safe बनाना गलती थी, और यह गलती higher-level languages ने भी की है
    https://doc.rust-lang.org/stable/std/env/fn.set_var.html
    glibc में environment modify होने के अधिक cases में getenv को safe बनाने वाला patch है, लेकिन C में अब भी environ को सीधे access किया जा सकता है, इसलिए modification होने वाली स्थिति में यह पूरी तरह safe नहीं हो सकता: https://github.com/bminor/glibc/commit/7a61e7f557a97ab597d6f...

    • यह हैरान करने वाला है कि glibc अब पुरानी versions को बनाए रखता है और exponential resizing policy अपनाता है
      हर active environment variable के लिए amortized basis पर constant-size memory leak बनता है, लेकिन कहा जाता है कि variable itself में भी पहले से ऐसी leak है, और वह लंबाई के हिसाब से बदलती भी है तथा उन values को भी शामिल करती है जो अब इस्तेमाल नहीं होतीं
      API के लिहाज़ से सही programs में भी इसके कारण memory अनंत रूप से बढ़ने वाले pathological use cases ज़रूर होंगे
      यह दिलचस्प लेकिन असहज है कि API को कई threads से इस्तेमाल कर rule तोड़ने वाले programs को ठीक करने के लिए, API का पालन करने वाले programs में unbounded memory growth जैसा bug introduce किया जा रहा है। dogma से ज़्यादा pragmatism जैसा लगता है
    • अगर standard library implementation synchronization handle कर सकती है, तो unsafe की मांग क्यों होनी चाहिए, यह सवाल है
  • भले ही C standard library maintainers setenv को multi-thread safe बनाने का विरोध करें, कम से कम POSIX के भीतर, या पहले de facto standard बनाकर POSIX से बाद में उसे स्वीकार करवाकर, नई thread-safe API define की जानी चाहिए
    अगर “कुछ नहीं किया जा सकता” के कारण समझाने में लगाया समय इस समस्या को ठीक करने में लगाया गया होता, तो पुरानी setenv का replacement बन चुका होता और कई software projects में उसे deprecate व remove किया जा सकता था
    glibc इस समस्या को practically खत्म करने की दिशा में बदलाव कर रहा है, यह देखते हुए Musl maintainer की यह बात भी कम convincing लगती है कि Musl के अंदर इसे ठीक नहीं किया जा सकता

    • सबसे बड़ी समस्या thread-safe API का न होना नहीं, बल्कि extern char **environ; का मौजूद होना है
      जब तक environ publicly accessible है, कोई guarantee नहीं कि setenv और getenv का इस्तेमाल होगा ही। क्योंकि दोनों mandatory नहीं हैं
      अगर environ को हटाया जा सके, तो setenv और getenv को thread-safe बनाना काफ़ी आसान है। अगर नहीं हटाया जा सकता तो असंभव है, लेकिन यह तर्क दिया जा सकता है कि complete solution न होने पर भी setenv और getenv को thread-safe बनाना improvement है
    • ऐसा लगता है कि जो भी exec() functions environment को argument के रूप में नहीं लेते या executable खोजने के लिए PATH search करते हैं, उन्हें भी locking की जरूरत पड़ेगी
    • यह बात convincing नहीं है कि आप उन experts से ज़्यादा जानते हैं जिन्होंने निष्कर्ष निकाला है कि इसे backward-compatible तरीके से ठीक नहीं किया जा सकता
  • Linux पर environment variables से जुड़े bugs का सामना करना एक तरह की रस्म-ए-गुज़र जैसा लगता है, और दूसरे Unix पर अजीब तरह से यह कम समस्या बनता है
    Linus और kernel POSIX bugs को व्यावहारिक तरीके से ऐसे ठीक करते हैं कि वे असल में फटें नहीं, लेकिन यह थोड़ा मज़ेदार है कि glibc अब भी पीछे है, जबकि लोगों को समस्या को थोड़ा भी कम करने की कोशिश किए दशकों हो चुके हैं
    TZ जैसी तमाम सिरदर्दी चीज़ें हैं, यह सही है, लेकिन अगर getenv_r() उपलब्ध कराया जाता, उसे setenv() के साथ synchronize किया जाता, और getenv() इस्तेमाल करने पर compile/link चरण में सिर्फ warning दे दी जाती, तो काफी समस्याएँ गायब हो जातीं
    इससे भी आगे, environment pointer को read-only रखते हुए copy-on-write (COW) तरीका भी अपनाया जा सकता था
    इसके बजाय समस्या को अलग-अलग applications पर धकेल दिया गया, जो बड़ी गलती है क्योंकि application authors को dependencies क्या कर रही हैं, यह लगभग पता नहीं चल सकता। बहुत पहले मेरी स्थिति भी ऐसी ही थी, और उस समय closed-source library vendor ने कहा था कि उस खिलौना Unix clone, Linux, का इस्तेमाल बंद कर दो

    • “Linux पर environment variable से जुड़ा bug है और दूसरे Unix पर समस्या कम है” यह आकलन कैसे निकला, समझ नहीं आता
      समस्या implementation नहीं, बल्कि API खुद है। setenv(), unsetenv(), putenv(), खासकर environ, multi-threaded programs में मूल रूप से unsafe हैं
      getenv_r() भी पूरी तरह बचा नहीं सकता। क्योंकि जब एक thread environment variable की पुरानी value दिए गए buffer में copy कर रहा हो, उसी दौरान दूसरा thread setenv() call कर सकता है
      बेशक, getenv() से value लेने के बाद अगर दूसरा thread setenv() call करके उस memory को invalidate कर दे, तो उसे getenv_r() ठीक कर देता है, लेकिन API को तोड़ने वाली दूसरी calls को रोकने का कोई तरीका नहीं है
      libc getenv()/setenv()/putenv()/unsetenv() के अंदर mutex पकड़कर कुछ समस्याएँ कम कर सकता है, लेकिन libc अब भी यह guarantee नहीं दे सकता कि getenv() द्वारा लौटाई गई value calling code के इस्तेमाल करने तक valid रहेगी
      environ पर direct access को safe बनाने का भी कोई अच्छा तरीका नहीं है। environ को thread-local बनाया जा सकता है, लेकिन तब हर thread का environment view स्थायी रूप से अलग हो सकता है और getenv_r() call तथा environ को सीधे देखने के नतीजे भी अलग हो सकते हैं
      यहाँ backward compatibility बनाए रखना सचमुच मुश्किल है, और सिर्फ functions को protect करने वाला mutex जोड़ना भी existing programs का अर्थ बदलकर उन्हें तोड़ सकता है
  • पहले setenv के भयानक होने पर एक लेख था: https://www.evanjones.ca/setenv-is-not-thread-safe.html
    इस पर चर्चा भी हुई थी और पहले comment से ही बात थी कि यह Rust में समस्या पैदा करता है: https://news.ycombinator.com/item?id=38342642

    • वह तो पहले से पता तथ्य है
      यहाँ बाकी अधिकांश समस्याएँ development environment जैसी लगती हैं। Amazon data center की remote machine पर Docker से test किया गया, और वह equipment process crash report नहीं कर पाया
      इसके अलावा container के अंदर backtrace पाने के लिए पर्याप्त debug symbol information भी नहीं थी। अगर पहली failure पर साफ backtrace मिल गया होता, तो बात तुरंत स्पष्ट हो जाती
      शुरुआत में setenv इस्तेमाल करने की ज़रूरत ही क्यों है, यह भी सवाल है
  • यह बात देखकर मुझे पुराने कुछ सहकर्मियों का बहुत भरोसे वाला 12-factor app आंदोलन याद आ गया। उन “factors” में से एक यह था कि application configuration environment variables से करनी चाहिए
    मुझे यह हमेशा थोड़ा मूर्खतापूर्ण लगा, क्योंकि configuration का तरीका एक flat namespace में string-type values को टोकरी की तरह डालने वाली संरचना है
    getenv()/setenv()/environ के खतरे भी environment variables को configuration के लिए न इस्तेमाल करने का मजबूत आधार लगते हैं
    बेशक हमेशा कोई शानदार और अच्छे से supported विकल्प मौजूद नहीं होता। मुझे configuration files पसंद हैं, और development·staging·production values भरने वाले template configurations भी इस्तेमाल किए जा सकते हैं। आम तौर पर कमियों और pitfalls के बावजूद YAML इस्तेमाल करता हूं, लेकिन भले ही कोई बेहतर configuration file format हो सकता है, मुझे YAML environment variables से कहीं बेहतर लगता है

    • Windows और Microsoft के प्रति मजबूत विरोध बहुत है, लेकिन समय के साथ उनके API design के सही साबित होने के मामले काफी मिलते हैं
      NT में environment variables को typed और templated बनाया जा सकता है, और Registry भी है, जो namespace वाला configuration database है। हालांकि वह verbose और अजीब है
      इसके अलावा MSVC लगभग सभी standard library functions के thread-safe versions देता है
      नए C/C++ developers को MSVC की POSIX compatibility की कमी पर अफसोस जताते अक्सर सुनता हूं, लेकिन लगता है वे गहराई से नहीं सोचते कि इसका असल मतलब क्या है। यह ज्यादा कुछ 1990s में लिखे C programs के साथ cross-compatible होने की इच्छा जैसा है
    • Environment variables को लेकर मेरी भी मिलती-जुलती चिंता है। यह बात पसंद नहीं कि उन्हें कहीं से भी पढ़ा जा सकता है
      यह किसी function के signature भर से behavior का अनुमान लगाने की क्षमता में बाधा डालता है, और कई functions को impure बना देता है जो pure functions हो सकते थे
      अगर कोई language feature हो जो किसी process में environment variables का इस्तेमाल न होने दे और उन्हें सिर्फ एक बार—variable-by-variable नहीं, बल्कि एक बार batch में ही पढ़ने दे—तो मैं उसे हर जगह इस्तेमाल करूंगा
    • getenv() अपने आप में पूरी तरह ठीक है, समस्या setenv() है
      सिद्धांततः उस रहस्यमय app के शुरू होने से पहले environment set हो चुका होता है, इसलिए इसे इस्तेमाल करने की जरूरत नहीं होनी चाहिए
      लेकिन flat namespace, string values, और यह कि कौन-सी libraries और modules अंदर आएंगे इसका पता न होते हुए भी सभी द्वारा share किया जाने वाला free-for-all global space—ये बातें setenv() की safety problem न भी हो, तब भी अच्छी idea नहीं हैं
    • “12-factor app” में एक appendix होना चाहिए कि process के जीवित रहते हुए environment को read-only मानना चाहिए
      यहां लोग जिन समस्याओं की बात कर रहे हैं, उनमें से ज्यादातर environment को mutable global state के लिए key-value store की तरह abuse करने से आती लगती हैं। समझ नहीं आता कोई ऐसा क्यों करना चाहेगा
      JVM environment को व्यवहार में immutable मानता है, और संभव है कि SoundCloud जैसी Scala·Java इस्तेमाल करने वाली कंपनियों ने 12-factor app आंदोलन को प्रभावित किया हो। मैंने कभी environment बदलने या threading issues पैदा करने वाली घटना नहीं देखी
      Environment बदल भी जाए तो JVM start के समय बनी immutable copy वैसी ही रहती है, और सामान्य Java API से environment के साथ interaction करने वाला code उस modification को नहीं देखता
      Configuration files की समस्या यह है कि parsing process-by-process होती है। इसलिए Linux/Unix इतना उलझा हुआ है। हर tool के configuration conventions और mechanisms अलग हैं, कोई standard नहीं है
      Docker ecosystem में container के अंदर आप जो भी करें, बाहर के साथ interface या तो volumes mount करके हर app की complex configuration शैली follow करना है, या बस environment variables इस्तेमाल करना है
      आजकल Docker पर चलने वाला अधिकांश modern software इतना Docker-friendly है कि environment से उसका behavior पूरी तरह control किया जा सकता है, और कई मामलों में यह पर्याप्त होता है
      Docker Compose या Kubernetes इस्तेमाल करने पर process start करने का तरीका define करने वाली environment variables की list YAML file में होती है, इसलिए कुछ हद तक मनचाही structure मिल भी जाती है। मुझे YAML पसंद नहीं, लेकिन यह काफी काम करता है, और syntax issue दिन खराब कर सकता है, पर alternatives भी बेदाग नहीं हैं
    • यह असल में अलग समस्या है। Environment variables को configuration के रूप में read करके फिर दोबारा न छुएं तो यह पूरी तरह safe है
      मैं भी 12-factor app style इस्तेमाल करता हूं, लेकिन app में आने के बाद environment variables और data validate करके store कर देता हूं। उसके बाद कोई समस्या नहीं होती
  • कम नजर आने वाले bug की तह तक जाने वाला शानदार लेख है
    intermittent bug, architecture-specific behavior, dependency के अंदर छिपा होना, Rust, Python GIL, gettext—सब कुछ इसमें था
    ऐसी detailed troubleshooting reports खुद अनुभव करने के सबसे करीब की सामग्री होती हैं। जब dependency वह चीज इस्तेमाल कर रही हो और मुझे कैसे पता चलता वाली स्थिति हो, तो “बस X इस्तेमाल मत करो” कहना आसान नहीं होता

  • वे कहते हैं, “nightly CI machine Amazon AWS पर चलती है, और फायदा यह है कि container नहीं बल्कि असली root user इस्तेमाल किया जा सकता है”, और साथ ही “container के बाहर जरूरी files नहीं हैं और container बहुत minimal है, इसलिए gdb आसानी से install नहीं किया जा सकता”
    क्या अब लोगों ने cloud और containers के बिना local पर build और debug करने की क्षमता खो दी है?

    • सही है। Cloud SaaS ने लोगों की समझ को कितना विकृत कर दिया है, यह चौंकाने वाला है
      बहुत छोटी-सी चीज करने के लिए भी तमाम cloud complexity और deployment layers जरूरी हो गई हैं। PC revolution को 100% उलटकर हम भद्दे और महंगे mainframe computing के दौर में लौट आए हैं
      वजह यह है कि पैसा cloud में है, और cloud DRM है। Software को वहां डाल दें तो subscription fee ली जा सकती है, बचना संभव नहीं, और perfect lock-in हमेशा बनाए रखा जा सकता है। कई बार users अपना data भी बाहर नहीं निकाल पाते
      Product optimization के लिए real-time analytics भी आसानी से किया जा सकता है
      Computing architecture, business model के downstream में है। Mainframe पहली बार इसलिए मरा था क्योंकि internet नहीं था और PC सस्ते थे, लेकिन इसलिए भी कि vendors ने lock-in power का बड़ा हिस्सा खो दिया था
      अब बहुत अधिक profitable model को वापस जिंदा करने का तरीका मिल गया है। Users की परेशान करने वाली freedom खत्म हो गई है, और ईमानदारी से कहें तो users को ऐसी freedom मिल जाए तो वे अक्सर पैसा नहीं देते, जिससे quality software business खड़ा करना मुश्किल हो जाता है
    • यह ARM पर ही फूटने वाले random memory corruption जैसा issue है
      Local पर crash reproduce न हुआ होगा, इसकी संभावना बहुत है। Developer machines ज्यादातर x86 रही होंगी और वहां crash नहीं आया होगा
      Crash handling बेहतर होनी चाहिए थी, लेकिन लगता है उन्हें भी इस problem का अंदाजा है और यहां चर्चा का मुख्य बिंदु वह नहीं है
    • जाहिर है क्षमता खोई नहीं है, लेकिन हमारी machines पर crash नहीं हुआ
    • जिस architecture पर problem आती है उसे चलाने वाला device ही शायद न हो, तो local पर debug कैसे करेंगे? वैसे भी जिस वास्तविक environment में failure होता है, वहीं debug करना कहीं ज्यादा तेज होता है
  • बदल सकने वाली global state बुरी चीज़ है। दोस्त हों तो दोस्तों को बदल सकने वाली global state इस्तेमाल नहीं करने देते
    मुझे environment variables पसंद नहीं हैं। यह “Linux तरीका” है, लेकिन इसे महामारी की तरह टालता हूँ। ज़ोरदार सिफारिश करता हूँ
    libc भयानक है, और दुनिया को अब इससे आगे बढ़ जाना चाहिए

    • अगर environment variables को process के अंदर read-only माना जाए तो ठीक है
    • अगर “बदल सकने वाली global state बुरी” है, तो CPU और RAM भी फेंक देने चाहिए
    • जानना चाहूँगा कि विकल्प के तौर पर क्या सुझाते हैं
      समस्या Linux नहीं है, न ही बदल सकने वाली global state या resources, और न ही libc है
      समस्या यह है कि दफ्तर में काम ठीक से करने का समय नहीं मिलता। मसलन, समस्या फटने से पहले उसे GDB से पकड़ना हो तो मैनेजर को code और उस code द्वारा छुई जाने वाली हर चीज़ को लगातार debug और backtrace करने का समय देना होगा
      आधे-अधूरे code में बहुत ज़्यादा पैसा लगा हुआ है। दुखद है, लेकिन सच है
    • libc ने दुनिया को information age में धकेला था
    • जानना चाहूँगा कि आपका पसंदीदा विकल्प क्या है
  • ऐसी समस्याएँ इतनी ज़्यादा थीं कि आखिरकार LD_PRELOAD से getenv / setenv / putenv को patch कर दिया

    • क्या इसे उसी तरह environment leak करने वाले fixed implementation के रूप में किया, जैसा अभी-अभी glibc में गया है?