2 पॉइंट द्वारा GN⁺ 2024-08-05 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • क्रिप्टोग्राफिक कोड में महत्वपूर्ण constant-time गुण सिर्फ compiler optimization से भी टूट सकता है, इसलिए LLVM के अंदर warning patch डालकर जोखिमपूर्ण patterns खोजने का प्रयोग किया जा रहा है
  • compiler “optimization” कुछ benchmarks को तेज़ बना सकती है, लेकिन असली hot path अक्सर intrinsics और assembly पर निर्भर करते हैं, और optimization से पैदा हुए bugs की लागत अलग से जुड़ती रहती है
  • जून 2024 में Antoon Purnal ने पुष्टि की कि Kyber reference code, Clang 15 या उसके बाद के कुछ optimization options में secret value पर आधारित conditional branch में बदल सकता है, जिससे timing attack संभव हो सकता है
  • TIMECOP 2, SUPERCOP के अंदर constant-time घोषित compiled results की जांच करता है, लेकिन Valgrind-supported instructions और वास्तविक test execution में दिखने वाले data flow तक इसकी सीमाएँ हैं
  • व्यावहारिक जवाब यह है कि crypto_{int,uint}{8,16,32,64}.h जैसी functions का उपयोग कर compiler को 1-bit result को bool की तरह देखने से रोका जाए, या verified assembly, security-oriented languages, और dedicated compilers की ओर जाया जाए

compiler “optimization” से बनता ज़िम्मेदारी का खालीपन

  • हाल के LLVM और GCC change logs में “optimization”, “optimization” tests, test fixes, और “optimization” bug fixes लगातार दिखाई देते हैं
  • compile होने से पहले सही चलने वाला code, compiler change के बाद बदल जाए, तब भी कई मामलों में ज़िम्मेदारी उस programmer पर डाल दी जाती है जिसने “undefined behavior” को छुआ हो
  • ऐसे “language standards” compiler writers बनाते हैं, और नतीजतन लाखों programmers का code, compiler writers के छोटे समूह के बदलावों से पैदा हुई ज़िम्मेदारी ढोता है
  • क्रिप्टोग्राफिक code के उदाहरण में, कई CPU benchmarks पर kyber768 का avx2 implementation, “optimized” compiler से compile किए गए portable code से लगभग 4 गुना तेज़ है

optimization performance measurement की सीमाएँ

  • 2000 में Todd A. Proebsting ने Proebsting's Law में कहा कि “compiler advances हर 18 साल में computing power को दोगुना करती हैं”, और निष्कर्ष निकाला कि compiler optimization का योगदान सीमांत है
  • Arseny Kapoulkine ने 2022 के benchmark में संक्षेप में बताया कि LLVM 11, LLVM 2.7 की तुलना में optimized compilation में 2 गुना अधिक समय लेता है, जबकि generated code आम तौर पर 10~20% तेज़ होता है
  • दोनों चर्चाएँ उस performance measurement को छोड़ देती हैं जिसे असली user महसूस करता है
    • performance-केंद्रित hotspots में intrinsics और assembly बहुत इस्तेमाल होते हैं
    • FFmpeg में .asm और .S files मिलाकर 160,000 lines assembly हैं
    • जैसे-जैसे computers और networks अधिक data संभालते हैं, वास्तविक CPU time ऐसे hotspots पर और अधिक केंद्रित होता जाता है
  • security cost भी optimization चर्चा के बाहर अलग से बढ़ती है
    • Deloitte ने बताया कि 2023 में IT security budget, corporate revenue का 0.5% था
    • 2022 में वैश्विक corporate total revenue 48 trillion dollars से अधिक होने के आँकड़े के साथ देखें, तो कुल पैमाना सैकड़ों अरब dollars का हो सकता है
    • हालाँकि यह भी ध्यान देने योग्य है कि Deloitte का 0.5% शायद company-wise simple average हो, और सभी कंपनियों ने survey का जवाब नहीं दिया था

timing leak और Kyber मामला

  • “optimized” compilers से बनने वाली security problems में सिर्फ पारंपरिक bugs ही नहीं, बल्कि timing leak भी शामिल है, जहाँ secret information execution time में रिसती है
  • Laurent Simon, David Chisnall, Ross Anderson का EuroS&P 2018 paper चेतावनी देता है कि compiler upgrade बिना चेतावनी के पहले से सुरक्षित code में timing channel खोल सकता है
  • 2018 paper में ज़ोर दिया गया उदाहरण वह code था जो bool से दो values में से एक चुनता था, और bool compiler को conditional jump बनाने के लिए प्रेरित करता था
    • cryptographic implementations में इससे बचने के लिए महत्वपूर्ण code से bool हटाया जाता है और अलग constant-time comparison functions बनाई जाती हैं
    • उद्धृत किया गया है कि OpenSSL इसके लिए 37 functions घोषित करता है
  • 2015 का curve25519-donna और MSVC 2015 मामला लेख में गलतफहमी के रूप में व्यवस्थित किया गया है
    • वास्तव में 32-bit x86 के लिए compile करते समय int64 operations, Microsoft की 32-bit int64 library llmul.asm call में बदल जाती थीं
    • timing leak, llmul.asm की data-dependent branch से पैदा हुई, और इस library को भी उचित source-code अवधारणा का हिस्सा माना जाना चाहिए
  • जून 2024 में Antoon Purnal ने पुष्टि की कि Kyber reference code, Clang 15 या उसके बाद के कुछ optimization options में timing attack की अनुमति दे सकता है
    • समस्या का रूप (-((x>>j)&1))&y था, जो x का j-वाँ bit set होने पर y, अन्यथा 0 बनाता है
    • Clang, bit-test instruction से उस bit को bool में बदलता है, और फिर उस bool के आधार पर conditional branch बनाता है
    • LLVM के अंदर lib/CodeGen/SelectionDAG/DAGCombiner.cpp का combineShiftAnd1ToBitTest यह “optimization” संभालता है
    • यह function Sanjay Patel ने सितंबर 2019 में जोड़ा था, और बाद में कई लोगों ने इसमें बदलाव किए
  • GCC में भी सीमा लाँघने जैसा एक समान मामला है
    • ARM के नवंबर 2021 के GCC patch ने (-x)>>31 को -(x>0) में बदला
    • अप्रैल 2024 में इस पर चेतावनी जारी हुई

TIMECOP और constant-time जांच

  • TIMECOP 2, SUPERCOP cryptographic test framework में built-in है, और constant-time घोषित compiled code में secret-value-derived conditional branches की स्वचालित जांच करता है
  • जांच का दायरा conditional branches के अलावा secret values से निकले array indexes को भी शामिल करता है
    • KyberSlash paper secret-value-derived division की जांच के लिए patch का भी वर्णन करता है
  • TIMECOP 1, Moritz Neikes द्वारा SUPERCOP में बदलाव कर बनाया गया tool था, जिसने Adam Langley के ctgrind approach को automate किया
  • TIMECOP 2 ने पुराने approach पर कुछ विस्तार किए
    • RNG output को अपने आप secret values के रूप में mark करता है
    • “declassification” को support करता है
    • “public inputs” designation को support करता है
    • कई cores पर चलता है
  • TIMECOP की स्पष्ट सीमाएँ हैं
    • यह सिर्फ Valgrind द्वारा supported instructions को संभाल सकता है, इसलिए AMD XOP instructions जैसी चीज़ों पर रुक जाता है
    • यह सिर्फ वही data flow जांचता है जो वास्तविक test execution में दिखाई देता है
  • constant-time behavior की जांच के tools पर काम जारी है, और संबंधित tools की सूची ct-tools पर है
  • TIMECOP जैसी जांच libmceliece test suite में शामिल की गई है, और यह अन्य libraries तक भी फैल सकती है

constant-time rewrite तरीका

  • variable-time code fragments खोज लेने के बाद, उन्हें bug के बिना constant-time में दोबारा लिखने का तरीका चाहिए
  • जुलाई 2024 की प्रस्तुति में libmceliece और SUPERCOP द्वारा दिए गए कुछ constant-time functions का परिचय कराया गया
    • file names हैं crypto_{int,uint}{8,16,32,64}.h
    • इन files को दूसरे projects में copy करके इस्तेमाल किया जा सकता है
  • उदाहरण function crypto_uint32_bitmod_mask(x,j) का effect -((x>>(j&31))&1) जैसा है, लेकिन compiler को 1-bit result देखने से रोकता है
  • अधिक जटिल उदाहरण के रूप में crypto_uint32_max(x,y) भी है
  • 2018 paper में Clang/LLVM में constant-time function __builtin_ct_choose(bool cond, x, y) जोड़ने वाले tweak की चर्चा है
    • paper ने गलत तरीके से सुझाव दिया कि सिर्फ यह एक function काफी होगा
    • यह function कभी compiler में आ सकता है, लेकिन किसी project के लिए उस पर निर्भर करना संभव होने में बहुत समय लग सकता है
    • इसके implementation approach को crypto_{int,uint}{8,16,32,64}.h से अधिक नाज़ुक माना गया है

समस्या को पहले से टालने के तरीके

  • यदि compiled library की pre-distribution testing, compiler-introduced timing leaks पकड़ ले, तो code rewrite के दौरान deployment में पुराना compiler version इस्तेमाल किया जा सकता है
    • यह users को सुरक्षित रखने वाला एक अस्थायी जवाब है
  • एक समाधान library को assembly में distribute करना है
    • RWC 2024 की प्रस्तुति Adoption of high-assurance and highly performant cryptographic algorithms at AWS तेज़ X25519 software दिखाती है, जिसके बारे में प्रमाणित है कि वह सभी inputs पर X25519 को सही तरह compute करता है
    • implementation, 64-bit Intel/AMD CPU के लिए 2 versions और 64-bit ARM CPU के लिए 2 versions वाली assembly में लिखा गया है
    • correctness statement उस machine code के बारे में है जिसे user वास्तव में चलाता है, और proof को HOL Light theorem prover से verify किया गया है
  • लेकिन जो cryptographic software अभी उस स्तर तक नहीं पहुँचा है, उसमें assembly की auditing difficulty अब भी बनी रहती है
  • C, C++ आदि में लिखे code में timing leaks रोकने वाली “vaccine” जल्दी डालने के तरीकों की भी खोज हो रही है

clang-vs-clang patch प्रयोग

  • x&1 और x>>31 में एक समानता है: इनके परिणाम की सिर्फ दो संभावनाएँ होती हैं
    • x&1 है 0 या 1
    • uint32 का x>>31 है 0 या 1
    • int32 का x>>31 है 0 या -1
  • ऐसी shapes compiler “optimization” लिखने वालों के लिए 1-bit result को bool में डालना आसान बना देती हैं
  • यह सिफारिश की गई है कि हमेशा -fwrapv के साथ compile करें ताकि GCC और Clang, two's-complement arithmetic मानें
  • सिर्फ source में &1, 1&, >>31 आदि scan करने पर भी कई examples मिल जाते हैं, लेकिन यहाँ LLVM “optimizer” में सीधे patch डालकर दूसरे तरीके से scan किया गया
  • patch LLVM commit 68df06a0b2998765cb0a41353fcf0919bbf57ddb से शुरू होती है, &1 और >>31 खोजती है, और यह warning देती है
    • please take this away before clang does something bad
  • उदाहरण compile command है clang -Rpass-analysis=clang-vs-clang -O -c x.c
  • test function इस प्रकार है
int sra31(int x)
    {
      x >>= 31;
      return x;
    }
  • वही warning बार-बार दिखना आश्चर्यजनक नहीं है
    • compiler “optimization” तब तक बार-बार लागू करने की कोशिश करता है जब तक आगे कोई प्रगति संभव न रहे
  • clang-vs-clang output, shift में signed और unsigned का फर्क दिखाती है
    • यह फर्क crypto_{int,uint}{8,16,32,64}.h आधारित manual या automatic rewrite में महत्वपूर्ण है
    • source transformation automate करने के तरीकों में clang-tidy एक उदाहरण है
  • जो code #ifdef से हट चुका हो या इस “optimization” stage से पहले eliminate हो गया हो, वह clang-vs-clang warning नहीं बनाता

SUPERCOP run results और खोजे गए मामले

  • SUPERCOP 20240716 को dual EPYC 7742 पर ./data-do-biglittle से चलाया गया
    • overclocking disable थी
    • SUPERCOP compiler list में okcompilers/{c,cpp} की clang line में -Rpass-analysis=clang-vs-clang जोड़कर clang-vs-clang इस्तेमाल करने के लिए समायोजन किया गया
  • results 3 घंटे बाद तैयार हुए
    • Clang output कुल 675,752 lines था
    • raw size 210,786,494 bytes थी
    • compressed result 3,595,199 bytes का 20240803-fromclang.txt.gz है
  • output में public data आधारित source branches से, Clang के अंदर &1 बनने की वजह से बहुत noise है
  • साफ़ तौर पर पहले से बदल देने लायक उदाहरण यह है
a0 += (a0>>15)&106;
  • ऐसा उदाहरण जिसे साधारण source scan से पकड़ने के लिए C parsing effort चाहिए, यह है
    • macro ONE8 को ((uint8_t)1) के रूप में define किया गया है
*pk2^=(((* pk_cp)>>ir)&ONE8)<<jr;
  • और कठिन उदाहरण AVX2 intrinsic-आधारित macro से आता है
    • signmask_x16(x) को _mm256_srai_epi16((x),15) के रूप में define किया गया है
    • यह 256-bit vector के भीतर हर signed 16-bit chunk को 15 bits right shift करता है
mask = signmask_x16(sub_x16(x,const_x16((q+1)/2)));
  • यह AVX2 मामला फिलहाल उच्च प्राथमिकता का नहीं है
    • vector operation को conditional branch में बदलने के लिए AVX-512 से compile करना होगा, और compiler को vectorized bool को serial bool conditional branches में बदलने जैसा अजीब फैसला लेना होगा
    • TIMECOP, Valgrind का उपयोग करता है, और Valgrind AVX-512 को support नहीं करता
    • अभी के लिए AVX-512 compilation की सिफारिश नहीं की जाती

int128 और व्यापक प्रतिक्रिया की दिशा

  • सबसे रोचक खोज वह मामला था जहाँ int128 का 64-bit right shift, >> warning पैदा कर रहा था
  • int128 implementation अंदरूनी तौर पर upper 64-bit word का sign जानने के लिए 63-bit right shift का उपयोग कर सकता है
  • अगर Clang, GCC की तरह 63-bit right shift को bool और फिर conditional branch में बदलने का support जोड़ दे, तो बहुत सा int128 code अचानक variable-time बन सकता है
    • इस स्थिति में 2015 paper के title में दावा किया गया परिदृश्य जैसा कुछ होगा, लेकिन इस बार source में bool न होते हुए भी
  • source level पर सबसे आसान सुरक्षा यह है कि compiler के मौजूदा int128 implementation से बचा जाए और crypto_int128 functions इस्तेमाल किए जाएँ
    • crypto_int128, GCC और Clang के int128 से अलग, छोटे 32-bit platforms पर भी काम कर सकता है
  • GCC और Clang में secret data types जोड़ने का विचार अच्छा लगता है, लेकिन दोनों compilers की संरचना में इसे मज़बूती से बनाने का रास्ता स्पष्ट नहीं दिखता
  • उम्मीद उन compilers से अधिक है जिन्हें शुरू से security के लिए design किया गया है
    • नए input language की माँग करने वाले security-focused compilers में FaCT और सक्रिय रूप से विकसित हो रहा Jasmin शामिल हैं
    • code rewrite समय को लेकर चिंता है, लेकिन मौजूदा compilers जिस तरह existing code को संभालते हैं, उसे देखते हुए किसी न किसी रूप में कार्रवाई ज़रूरी लगती है

1 टिप्पणियां

 
GN⁺ 2024-08-05
Hacker News पर टिप्पणियां
  • जिस code ने undefined behavior किया हो, उसके मनचाहे तरीके से न चलने को compiler bug कहना सही नहीं है
    यह कुछ वैसा ही है जैसे गलत arguments के साथ dd चलाकर data उड़ा देना और फिर कहना कि dd में bug है

    • लगता है लेखक ने यहां implementation-defined behavior और undefined behavior को मिला दिया है। लेख के उदाहरणों में ज़्यादातर valid code है, और असल समस्या यह है कि bit-operation arithmetic को branches में बदलने वाली compiler optimization crypto code में timing attacks को संभव बना देती है
      source code या compiler को bug कहना मुश्किल है; कहना बेहतर होगा कि C standard, लेखक के मानकों के हिसाब से, बहुत कम specified है और कुछ targets पर security bugs पैदा कर देता है
      आखिर C standard के लेखक hardware के behavior तक define नहीं कर सकते, वे सिर्फ language semantics define कर सकते हैं, इसलिए crypto वालों को hardware की वजह से होने वाले bugs से जूझना ही पड़ता है
    • समस्या यह है कि C और C++ में undefined behavior बेहिसाब ज़्यादा है, और सबको avoid करना बेहद कठिन है
      Rust के फायदों में से एक यह है कि वह संभावित undefined behavior को unsafe blocks के अंदर सीमित कर देता है। फिर भी, C में जो कई चीजें undefined behavior हैं उन्हें Rust ने define कर रखा है, लेकिन unsafe code में जाते ही सूक्ष्म undefined behavior पर गलती से पैर रख देना बहुत आसान है
    • compiler users के लिए उपयोगी undefined behavior model सिर्फ दो ही हैं: अगर विचार खराब है तो compile करने से मना कर दो, या कोई reasonable और stable काम करो
      तीसरा model, जिसमें चुपचाप fail होकर unpredictable code generate होता है, सिर्फ compiler authors के लिए उपयोगी है। spec के पीछे छिपना असली users के लिए फायदेमंद नहीं है
    • Russ Cox का लेख C and C++ Prioritize Performance over Correctness इस विषय को अच्छी तरह कवर करता है: https://research.swtch.com/ub
    • वह rebuttal काफ़ी हद तक straw man पर हमला करने जैसा है। मुख्य बात यह है कि compiler authors खुद तय करते हैं कि क्या undefined behavior है, और standard को इस तरह define करते हैं कि उन्हें अधिक optimization room मिले
      वह optimization उस code को तोड़ देती है जो पहले ठीक से चलता था। compiler authors backward compatibility को प्राथमिकता दे सकते थे, लेकिन वे ऐसा नहीं करते
      ऊपर से ऐसी optimizations वास्तविक code performance को meaningful तरीके से सुधारती भी नहीं हैं, इसलिए code तोड़ने वाले trade-off की कीमत नहीं है—इस दावे का जवाब देना होगा
  • Bernstein मुझे पसंद हैं, लेकिन कभी-कभी वे गलत दिशा पकड़कर उग्र हो जाते हैं, और यह लेख उसका अच्छा उदाहरण है। लेख के अंत में वे खुद भी आधा-सा मानते हैं
    लेख का बड़ा हिस्सा इस secondary point पर है कि optimization gains कितने अच्छे हैं, और data होने पर भी यह use case के हिसाब से बदलने वाला judgement है
    मुख्य शिकायत यह है कि C compiler उन semantics को consider नहीं करता जिन्हें भाषा में express नहीं किया जा सकता, और यह कोई हैरानी की बात नहीं है
    अंत में वे कहते हैं, “ऐसी भाषा इस्तेमाल करें जो जरूरी semantics express कर सके”; पूरा लेख उसी एक वाक्य से बदला जा सकता था

    • अहम बात यह है कि C और C++ semantics define करने वाले बहुत सारे behavior को “undefined behavior” की टोकरी में डाल देते हैं
      उनमें से काफी का आधार संदिग्ध है, और वे सही program लिखना और कठिन बना देते हैं
    • optimization gains use case पर depend करते हैं—यह हिस्सा उपयोगी context था और काफी आंखें खोलने वाला था
    • यहां DJB ज्यादा persuasive नहीं थे। बिना आधार वाले elitist religious views काफी झलक रहे थे
  • C और C++ constant-time guarantees वाले algorithms लिखने के लिए उपयुक्त नहीं हैं
    standard में real-time की अवधारणा लगभग नहीं है, और compiler भी extensions के रूप में अतिरिक्त guarantees नहीं देते
    लेकिन इसका दोष compiler developers पर डालना गलत दिशा है

    • अगर आप ऐसा machine code बनाना चाहते हैं जो branches से स्वतंत्र होकर हमेशा constant time में operations करे, तो आपको ऐसी भाषा इस्तेमाल करनी चाहिए जो इसे express कर सके। C इसका support नहीं करता
    • जानना चाहूंगा कि constant-time guarantees वाले algorithms लिखने के लिए कौन-सी language उपयुक्त है
  • Intel CPU पर clang हो या कुछ और, user mode में सही code generate नहीं कर सकता। क्योंकि शुरुआत से ही कोई सही code मौजूद नहीं है
    https://www.intel.com/content/www/us/en/developer/articles/t...
    document में DOITM देखें—user-space crypto library के लिए जरूरी bit set करना simply impossible है

    • user-mode code भी सही mode में run हो सकता है। बस उस mode को on/off करने वाला toggle खुद नहीं कर सकता
      एक बार on हो जाए तो user space में भी ठीक चलता है, इसलिए जैसे prctl system call से enable होने वाला per-process flag हो सकता है, और scheduler task switch के समय MSR adjust कर सकता है
    • क्या kernel में system call करके flag set करने के बाद, उसी state में user mode में वापस नहीं आ सकते?
  • सिर्फ़ यह वाक्य देखकर ही—“जब भी संभव हो, compiler लिखने वाले अपने बनाए bugs की ज़िम्मेदारी लेने से इनकार करते हैं”—ब्लॉग पोस्ट की विशेषज्ञता इतनी जल्दी ढहते देखना दुर्लभ है
    अगर link तक जाकर देखें, तो यह बस C की बेहद बुनियादी बात है कि undefined behavior का मतलब “कोई भी arbitrary value” बनना नहीं होता

    • लगता है दोनों अलग-अलग चीज़ों को देखकर “bug” कह रहे हैं। एक पक्ष source code के bug की बात कर रहा है, और दूसरा generated program के bug की
      undefined behavior होने पर भी source code bug हो सकता है, लेकिन generated program फिर भी अक्सर सही हो सकता है। बाद में जब compiler लेखक नया optimization डालकर उसी undefined behavior के आधार पर bug वाला program generate करता है, तब ज़िम्मेदारी को लेकर बहस शुरू होती है
      जिस हिस्से को लोग मानना नहीं चाहते, वह यह है कि users के प्रति ज़िम्मेदारी सभी पक्षों में बंटी होती है। अगर किसी CRUD app ने NULL dereference किया और सिर्फ़ इसी वजह से battery जल गई, तो कोई सामान्य इंसान सिर्फ़ app लेखक को NULL check भूलने के लिए दोषी नहीं ठहराएगा
      Compiler, operating system और hardware कंपनियों को भी अपने गैर-ज़िम्मेदाराना ढंग से design किए गए products की ज़िम्मेदारी लेनी चाहिए; ISO standard में “undefined behavior” कह देने भर से बात खत्म नहीं हो जाती। supply chain के सभी सदस्य यह अनुमान लगाने और उचित रूप से संभालने की साझा ज़िम्मेदारी रखते हैं कि product का दुरुपयोग कैसे हो सकता है
    • मुझे लगता है लेखक undefined behavior क्या है, यह अच्छी तरह जानता है। वह बस पूरे system को critical नज़र से देख रहा है
      undefined behavior value देने के लिए मौजूद होता है। उसके बिना भी भाषा बनाई जा सकती है, लेकिन उसे रखने की वजह portability और compiler लेखकों को मिलने वाली flexibility है
      लेख का मुख्य सवाल यह है कि क्या यह flexibility, undefined behavior के बिना program लिखने की कठिनाई की तुलना में वाकई मूल्यवान है
      लेखक को लगता है कि bugs से खोया पैसा तेज़ bytecode से बचाए गए पैसे से ज़्यादा है, और language standard में क्या जाएगा यह तय करते समय compiler लेखकों का प्रभाव बड़ा होता है, इसलिए इसे सुधारने की इच्छाशक्ति कम है
  • संदर्भ के लिए, clang में हर function के लिए सभी optimizations बंद करने वाला clang::optnone attribute है, और GCC में नाम से optimizations जोड़ने/हटाने या compiler flags से स्वतंत्र रूप से optimization level तय करने वाला बढ़िया gnu::optimize attribute है
    gnu::optimize(0) उस clang flag जैसा ही है। clang में खास तौर पर memcpy और memset optimizations बंद करने वाला clang::no_builtins भी है

  • crypto वाले लोग जिन लक्ष्यों को चाहते हैं, जैसे constant-time evaluation और secret values छिपाना, उनसे मैं कुछ हद तक सहमत हूं
    लेकिन general-purpose compiler ज़्यादातर समय ऐसी चीज़ों के बारे में नहीं सोचता, इसलिए यह आम तौर पर चल जाने वाले hack से ज़्यादा बनना मुश्किल लगता है
    अगर इसे गंभीरता से करना है तो शायद अपना special-purpose compiler चाहिए, या फिर assembly पर ही चलते रहना होगा

    • लेखक ने ऐसा compiler पहले ही लिखा है: https://cr.yp.to/qhasm.html कम से कम वह ऐसा prototype है
  • शायद कभी हम आज के दौर को पुराने बुरे दिनों की तरह देखेंगे, और C से आगे बढ़कर ऐसी भाषा इस्तेमाल कर रहे होंगे जिसमें undefined behavior कहीं कम होगा
    C में ऐसे expressions लिखना बहुत आसान है जो compile तो हो जाते हैं, लेकिन compiler के लिए लेखक की मंशा समझना लगभग नामुमकिन होता है
    उदाहरण के लिए Python में result = [something(value) for value in set_object] जैसा code लिखा जा सकता है। set object unordered होता है, इसलिए यह साफ है कि items को process करने का क्रम और result का क्रम महत्वपूर्ण नहीं है, और इससे compiler को लेखक की मंशा का अनुमान लगाए बिना language level पर कई optimizations के रास्ते खुलते हैं
    immutable data वाली दूसरी languages में similar code एक कदम और आगे जाता है: something(value1) something(value2) को प्रभावित नहीं कर सकता, इसलिए उसे threads हों या processes, parallel में चलाया जा सकता है
    C compiler optimization का बड़ा हिस्सा code patterns देखकर यह पता लगाने की कोशिश है कि लेखक शायद क्या करना चाहता था और उसे तेज़ कैसे किया जाए। Modern languages की तुलना में C में intent व्यक्त करने की क्षमता कम है, इसलिए अनुमान लगाने की आज़ादी तो है, लेकिन decent performance पाने के लिए ऐसा inference करना पड़ता है
    फिर भी यह Hubble telescope को चश्मे की जरूरत पड़ने वाली घटना की तरह भेष में blessing भी हो सकता है। सीमाओं को पार करने के लिए बेहतरीन techniques बनीं, और समस्या ठीक होने के बाद उन्हीं techniques ने मूल उम्मीद से कहीं ज़्यादा performance दी। C compiler optimizations को non-C languages पर लागू किया जाए तो वे superpower की तरह काम कर सकती हैं

    • Python उदाहरण की कमी यह है कि भले ही order specified न हो, लोग किसी property पर निर्भर हो सकते हैं, और optimizer अगर order बदल दे तो code टूट सकता है
      मूल रूप से यह undefined behavior जैसा है, लेकिन तुरंत safety issue के बजाय गलत result के रूप में दिख सकता है। बेशक गलत result आगे चलकर safety issue बन सकता है
      undefined behavior के उलट, ऐसा “sanitizer” बनाना व्यावहारिक रूप से असंभव है जो जांचे कि code सभी संभव set orders में काम करता है या नहीं
      gcc और clang में कई low-level hints हैं जो दूसरी languages में अक्सर नहीं मिलते। __builtin_expect/__builtin_unpredictable, __builtin_unreachable/__builtin_assume, #pragma clang loop vectorize(assume_safety)/#pragma GCC ivdep, loop unrolling या vectorization बंद करने या किसी खास value को चुनने वाले pragma आदि
      मेरे हिसाब से सबसे बड़ी कमी optimization barrier की है, जो स्पष्ट रूप से compiler को किसी value के source के आधार पर inference करने से रोक सके। __asm__ कुछ हद तक यह कर सकता है, लेकिन उसके unwanted side effects हैं और platform-specific register type names की जरूरत पड़ती है
      high-level intent-based optimization की संभावना भी साफ है। जैसे loop में n बार push करने से पहले array list space reserve करना, same key के साथ contains→get→put करने वाली hashmap lookup को merge करना, या global allocation behavior को local तौर पर infer करके objects और allocations हटाना
    • सिद्धांत रूप में बात सही लगती है, लेकिन असल में C से तेज़ होने का प्रमाण किसी ने नहीं दिया
      C real hardware के पर्याप्त करीब है, इसलिए programmer सीधे बता सकता है कि क्या करना है; compiler को programmer की मंशा का अनुमान लगाने की जरूरत नहीं पड़ती
    • यह सही है कि semantics-based optimization की गुंजाइश है, लेकिन observation के हिसाब से ऐसे optimizations ज्यादातर memory allocation के आसपास होते हैं
      ऐसी memory optimizations implement करने वाली languages आम तौर पर Java जैसी होती हैं, और उनमें शुरू से ही aggressive upfront pessimization होती है, इसलिए ऐसी optimization करने की प्रेरणा बनती है। लेकिन उन optimizations से भी नुकसान की भरपाई नहीं होती
      मुद्दा यह है कि C भी बहुत अच्छा नहीं है, लेकिन दूसरी तरफ हालत और खराब है
  • अगर C की semantics पसंद नहीं है, तो compiler engineers पर गुस्सा न करें, कोई दूसरी programming language इस्तेमाल करें

    • सच कहूं तो मुझे नहीं पता djb अपने qhasm के अलावा कुछ भी बर्दाश्त कर पाएगा या नहीं। Zig भी नहीं। यह review उसकी तरफ से इतना चौंकाने वाला नहीं है
  • यह एक ताज़ा लेख है जो ऐसा नजरिया देता है जो अक्सर सुनने को नहीं मिलता। साथ में देखने लायक: https://gavinhoward.com/2023/08/the-scourge-of-00ub/