პროგრამირების სამყაროში გავრცელებული მოსაზრებაა, რომ თუ კომპილატორს ერთსა და იმავე კოდს მიაწვდით, შედეგად იდენტურ ბინარულ ფაილს მიიღებთ. თუმცა, რეალობა ბევრად უფრო კომპლექსურია. Anubis-ის პროექტზე მუშაობისას ავტორი სწორედ ამ პრობლემის წინაშე აღმოჩნდა.

პროექტის მიზანია WebAssembly-ზე დაფუძნებული უსაფრთხოების მექანიზმების დანერგვა. იმისთვის, რომ კლიენტსა და სერვერს შორის ლოგიკა სინქრონში იყოს, ავტორმა გადაწყვიტა WebAssembly-ის JavaScript-ად გარდაქმნა, რაც სისტემას უფრო მოქნილს გახდიდა.

რატომ არის რეპროდუცირებადი ბილდები რთული?

თეორიულად, კომპილატორი დეტერმინისტული ფუნქციაა. პრაქტიკაში კი, ისეთი დეტალები, როგორიცაა __DATE__ და __TIME__ მაკროები, ბინარულ ფაილში კომპილაციის დროს აფიქსირებს. შედეგად, მიუხედავად იდენტური წყარო კოდისა, გამომავალი ფაილები განსხვავებულია.


კიდევ ერთი პრობლემა wasm-opt-ის ვერსიებთან იყო დაკავშირებული. Clang-ის კომპილატორი ფონურ რეჟიმში იყენებს ამ ხელსაწყოს ოპტიმიზაციისთვის. თუ სისტემაში დაყენებული ვერსია მოძველებულია, ბილდი ავტომატურად იშლება. ავტორს მოუწია --no-wasm-opt პარამეტრის გამოყენება, რათა თავიდან აეცილებინა შეუთავსებლობა.

მისამართების განლაგება და არაპროგნოზირებადი შედეგი

აღმოჩნდა, რომ Clang-ის მიერ გამოყენებული გამონაკლისების დამუშავების მექანიზმები მეხსიერების მისამართებზეა დამოკიდებული. ეს იწვევს იმას, რომ ბინარული ფაილი ყოველ ჯერზე დაახლოებით 29 ბაიტით განსხვავდება წინა ვერსიისგან. ეს განსხვავება დაფიქსირდა როგორც x86_64, ისე arm64 არქიტექტურებზე.

პრობლემის გადასაჭრელად ავტორმა ორი ნაბიჯი გადადგა:

  • გამოიყენა setarch --addr-no-randomize მისამართების რანდომიზაციის გასათიშად.
  • შექმნა დამოწმებული SHA256 ჰეშები კონკრეტული არქიტექტურებისთვის, რათა უზრუნველყოფილი იყოს ბილდის სანდოობა.

მიუხედავად იმისა, რომ სრული არქიტექტურათაშორისი რეპროდუცირება ამ ეტაპზე ვერ მოხერხდა, მიღწეულია დეტერმინისტული შედეგი ცალკეულ პლატფორმებზე, რაც პროექტის სტაბილურობისთვის საკმარისია.