Understanding Memory And Thread Safety Practices And .

2y ago
29 Views
3 Downloads
388.85 KB
17 Pages
Last View : 1m ago
Last Download : 3m ago
Upload by : Jenson Heredia
Transcription

Understanding Memory and Thread Safety Practicesand Issues in Real-World Rust ProgramsBoqin Qin Yilun Chen2Zeming YuBUPT, Pennsylvania State UniversityUSAPurdue UniversityUSAPennsylvania State UniversityUSALinhai SongYiying ZhangPennsylvania State UniversityUSAUniversity of California, San DiegoUSAAbstractKeywords: Rust; Memory Bug; Concurrency Bug; Bug StudyRust is a young programming language designed for systemssoftware development. It aims to provide safety guaranteeslike high-level languages and performance efficiency likelow-level languages. The core design of Rust is a set of strictsafety rules enforced by compile-time checking. To supportmore low-level controls, Rust allows programmers to bypassthese compiler checks to write unsafe code.It is important to understand what safety issues exist inreal Rust programs and how Rust safety mechanisms impactprogramming practices. We performed the first empiricalstudy of Rust by close, manual inspection of 850 unsafe codeusages and 170 bugs in five open-source Rust projects, fivewidely-used Rust libraries, two online security databases, andthe Rust standard library. Our study answers three importantquestions: how and why do programmers write unsafe code,what memory-safety issues real Rust programs have, andwhat concurrency bugs Rust programmers make. Our studyreveals interesting real-world Rust program behaviors andnew issues Rust programmers make. Based on our studyresults, we propose several directions of building Rust bugdetectors and built two static bug detectors, both of whichrevealed previously unknown bugs.ACM Reference Format:Boqin Qin, Yilun Chen, Zeming Yu, Linhai Song, and Yiying Zhang.2020. Understanding Memory and Thread Safety Practices andIssues in Real-World Rust Programs. In Proceedings of the 41stACM SIGPLAN International Conference on Programming LanguageDesign and Implementation (PLDI ’20), June 15ś20, 2020, London,UK. ACM, New York, NY, USA, 17 pages. https://doi.org/10.1145/3385412.33860361 IntroductionRust [30] is a programming language designed to build efficient and safe low-level software [8, 69, 73, 74]. Its mainidea is to inherit most features in C and C’s good runtimeperformance but to rule out C’s safety issues with strictcompile-time checking. Over the past few years, Rust hasgained increasing popularity [46ś48], especially in buildinglow-level software like OSes and browsers [55, 59, 68, 71, 77].The core of Rust’s safety mechanisms is the concept ofownership. The most basic ownership rule allows each valueto have only one owner and the value is freed when itsowner’s lifetime ends. Rust extends this basic rule with aset of rules that still guarantee memory and thread safety.For example, the ownership can be borrowed or transferred,and multiple aliases can read a value. These safety rulesessentially prohibit the combination of aliasing and mutability. Rust checks these safety rules at compile time, thusachieving the runtime performance that is on par with unsafelanguages like C but with much stronger safety guarantees.The above safety rules Rust enforces limit programmers’control over low-level resources and are often overkill whendelivering safety. To provide more flexibility to programmers,Rust allows programmers to bypass main compiler safetychecks by adding an unsafe label to their code. A function canbe defined as unsafe or a piece of code inside a function canbe unsafe. For the latter, the function can be called as a safefunction in safe code, which provides a way to encapsulateunsafe code. We call this code pattern interior unsafe.Unfortunately, unsafe code in Rust can lead to safety issuessince it bypasses Rust’s compiler safety checks. Adding unsafe code and unsafe encapsulation complicates Rust’s safetysemantics. Does unsafe code cause the same safety issues asCCS Concepts: · Software and its engineering Software safety; Software reliability. The work was done when Boqin Qin was a visiting student at PennsylvaniaState University.2 Yilun Chen contributed equally with Boqin Qin in this work.Permission to make digital or hard copies of all or part of this work forpersonal or classroom use is granted without fee provided that copies are notmade or distributed for profit or commercial advantage and that copies bearthis notice and the full citation on the first page. Copyrights for componentsof this work owned by others than ACM must be honored. Abstracting withcredit is permitted. To copy otherwise, or republish, to post on servers or toredistribute to lists, requires prior specific permission and/or a fee. Requestpermissions from permissions@acm.org.PLDI ’20, June 15ś20, 2020, London, UK 2020 Association for Computing Machinery.ACM ISBN 978-1-4503-7613-6/20/06. . . 15.00https://doi.org/10.1145/3385412.3386036763

PLDI ’20, June 15–20, 2020, London, UKBoqin Qin, Yilun Chen, Zeming Yu, Linhai Song, and Yiying Zhangtraditional unsafe languages? Can there still be safety issueswhen programmers do not use any łunsafež label in theircode? What happens when unsafe and safe code interact?Several recent works [2, 13, 28, 29] formalize and theoretically prove (a subset of) Rust’s safety and interior-unsafemechanisms. However, it is unclear how Rust’s languagesafety and unsafe designs affect real-world Rust developers and what safety issues real Rust software has. With thewider adoption of Rust in systems software in recent years,it is important to answer these questions and understandreal-world Rust program behaviors.In this paper, we conduct the first empirical study of safetypractices and safety issues in real-world Rust programs. Weexamine how safe and unsafe code are used in practice, andhow the usages can lead to memory safety issues (i.e., illegal memory accesses) and thread safety issues (i.e., threadsynchronization issues like deadlock and race conditions).Our study has a particular focus on how Rust’s ownershipand lifetime rules impact developers’ programming and howthe misuse of these rules causes safety issues, since these areRust’s unique and key features.Our study covers five Rust-based systems and applications (two OSes, a browser, a key-value store system, anda blockchain system), five widely-used Rust libraries, andtwo online vulnerability databases. We analyzed their sourcecode, their GitHub commit logs and publicly reported bugsby first filtering them into a small relevant set and thenmanually inspecting this set. In total, we studied 850 unsafecode usages, 70 memory-safety issues, and 100 thread-safetyissues.Our study includes three parts. First, we study how unsafe code is used, changed, and encapsulated. We foundthat unsafe code is extensively used in all of our studiedRust software and it is usually used for good reasons (e.g.,performance, code reuse), although programmers also tryto reduce unsafe usages when they can. We further foundthat programmers use interior unsafe as a good practice toencapsulate unsafe code. However, explicitly and properlychecking interior unsafe code can be difficult. Sometimessafe encapsulation is achieved by providing correct inputsand environments.Second, we study memory-safety issues in real Rust programs by inspecting bugs in our selected applications and libraries and by examining all Rust issues reported on CVE [12]and RustSec [66]. We not only analyze these bugs’ behaviorsbut also understand how the root causes of them are propagated to the effect of them. We found that all memory-safetybugs involve unsafe code, and (surprisingly) most of themalso involve safe code. Mistakes are easy to happen whenprogrammers write safe code without the caution of otherrelated code being unsafe. We also found that the scope oflifetime in Rust is difficult to reason about, especially whencombined with unsafe code, and wrong understanding oflifetime causes many memory-safety issues.Finally, we study concurrency bugs, including non-blockingand blocking bugs [80]. Surprisingly, we found that nonblocking bugs can happen in both unsafe and safe code andthat all blocking bugs we studied are in safe code. Althoughmany bug patterns in Rust follow traditional concurrencybug patterns (e.g., double lock, atomicity violation), a lot ofthe concurrency bugs in Rust are caused by programmers’misunderstanding of Rust’s (complex) lifetime and safetyrules.For all the above three aspects, we make insightful suggestions to future Rust programmers and language designers.Most of these suggestions can be directly acted on. For example, based on the understanding of real-world Rust usagepatterns, we make recommendations on good programmingpractices; based on our summary of common buggy codepatterns and pitfalls, we make concrete suggestions on thedesign of future Rust bug detectors and programming tools.With our empirical study results, we conducted an initialexploration on detecting Rust bugs by building two static bugdetectors (one for use-after-free bugs and one for double-lockbugs). In total, these detectors found ten previously unknownbugs in our studied Rust applications. These encouraging(initial) results demonstrate the value of our empirical study.We believe that programmers, researchers, and languagedesigners can use our study results and the concrete, actionable suggestions we made to improve Rust software development (better programming practices, better bug detectiontools, and better language designs). Overall, this paper makesthe following contributions. The first empirical study on real-world Rust programbehaviors. Analysis of real-world usages of safe, unsafe, and interiorunsafe code, with close inspection of 850 unsafe usagesand 130 unsafe removals. Close inspection of 70 real Rust memory-safety issuesand 100 concurrency bugs. 11 insights and 8 suggestions that can help Rust programmers and the future development of Rust. Two new Rust bug detectors and recommendations onhow to build more Rust bug detectors.All study results and our bug detectors can be found athttps://github.com/system-pclub/rust-study.2 Background and Related WorkThis section gives some background of Rust, including itshistory, safety (and unsafe) mechanisms, and its current support of bug detection, and overviews research projects onRust related to ours.2.1 Language Overview and HistoryRust is a type-safe language designed to be both efficientand safe. It was designed for low-level software developmentwhere programmers desire low-level control of resources764

Understanding Memory and Thread Safety Practices and Issues in Real-World Rust 62018# of bugs108002000KLOC# of ear20142016Figure 1. Rust History. (Each bluepoint shows the number of featurechanges in one release version. Eachred point shows total LOC in one release version.)Figure 2. Time of Studied Bugs.(Each point shows the number of ourstudied bugs that were patched duringa three month period.)(so that programs run efficiently) but want to be type-safeand memory-safe. Rust defines a set of strict safety rules anduses the compiler to check these rules to statically rule outmany potential safety issues. At runtime, Rust behaves likeC and could achieve performance that is close to C.Rust is the most loved language in 2019 according to aStack Overflow survey [49], and it was ranked as the fifthfastest growing language on GitHub in 2018 [45]. Becauseof its safety and performance benefits, Rust’s adoption insystems software has increased rapidly in recent years [3, 16,23, 59, 68, 76, 77]. For example, Microsoft is actively exploringRust as an alternative to C/C because of its memory-safetyfeatures [9, 44].Rust was first released in 2012 and is now at version 1.39.0.Figure 1 shows the number of feature changes and LOC overthe history of Rust. Rust went through heavy changes in thefirst four years since its release, and it has been stable sinceJan 2016 (v1.6.0). With it being stable for more than threeand a half years, we believe that Rust is now mature enoughfor an empirical study like ours. Figure 2 shows the fixeddate of our analyzed bugs. Among the 170 bugs, 145 of themwere fixed after 2016. Therefore, we believe our study resultsreflect the safety issues under stable Rust versions.6789101112 }f0(t0);// println!("{:?}", t0);if true {let t1 Test{v: 1};}// println!("{:?}", t1);(a) ownership & lifetime13 fn f2() {14 let mut t2 Test{v: 2};1516171819 }let r1 &t2;let mut r2 &mut t2;r2.v 3;// println!("{:?}", r1);(b) borrowFigure 3. Sample code to illustrate Rust’s safety rules.2.2Table 1. Studied Applications and Libraries. (Thestart time, number of stars, and commits on GitHub,total source lines of code, the number of memorysafety bugs, blocking bugs, and non-blocking bugs.libraries: maximum values among our studied libraries. There are 22 bugs collected from the twoCVE databases.)Software Start TimeServo2012/02Tock2015/05Ethereum s Commits LOC Mem Blk NBlk14574 38096 271K 14 13 181343462160K502556512121 145K 234457173897149K 143114502129199K 20233106240225K7610owner’s lifetime ends, the value will be dropped (freed). Thelifetime of a variable is the scope where it is valid, i.e., fromits creation to the end of the function it is in or to the endof matching parentheses (e.g., the lifetime of t1 in Figure 3spans from line 9 to line 10). This strict ownership rule eliminates memory errors like use-after-free and double-free,since the Rust compiler can statically detect and rejects theuse of a value when its owner goes out of scope (e.g., uncommenting line 11 in Figure 3 will raise a compile error).This rule also eliminates synchronization errors like raceconditions, since only one thread can own a value at a time.Under Rust’s basic ownership rule, a value has one exclusive owner. Rust extends this basic rule with a set of featuresto support more programming flexibility while still ensuring memory- and thread-safety. These features (as explainedbelow) relax the restriction of having only one owner forthe lifetime of a value but still prohibit having aliasing andmutation at the same time, and Rust statically checks theseextended rules at compile time.Ownership move. The ownership of a value can be movedfrom one scope to another, for example, from a caller toa callee and from one thread to another thread. The Rustcompiler statically guarantees that an owner variable cannotbe accessed after its ownership is moved. As a result, a callercannot access a value anymore if the value is dropped in thecallee function, and a shared value can only be owned byone thread at any time. For example, if line 7 in Figure 3 isuncommented, the Rust compiler will report an error, sincethe ownership of t0 has already been moved to functionf0() at line 6.Ownership borrowing. A value’s ownership can also beborrowed temporarily to another variable for the lifetime ofthis variable without moving the ownership. Borrowing isachieved by passing the value by reference to the borrowervariable. Rust does not allow borrowing ownership acrossthreads, since a value’s lifetime cannot be statically inferredacross threads and there is no way the Rust compiler canguarantee that all usages of a value are covered by its lifetime.Mutable and Shared references. Another extension Rustadds on top of the basic ownership rule is the support of multiple shared read-only references, i.e., immutable references1 #[derive(Debug)]2 struct Test {v: i32}3 fn f0( t: Test) {}4 fn f1() {5 let t0 Test{v: 0};2018YearPLDI ’20, June 15–20, 2020, London, UKSafety MechanismsThe goal of Rust’s safety mechanism is to prevent memoryand thread safety issues that have plagued C programs. Itsdesign centers around the notion of ownership. At its core,Rust enforces a strict and restrictive rule of ownership: eachvalue has one and only one owner variable, and when the765

PLDI ’20, June 15–20, 2020, London, UKBoqin Qin, Yilun Chen, Zeming Yu, Linhai Song, and Yiying Zhanginput self is borrowed immutably, but the value field ofself is changed through pointer p (an alias) at line 6.Many APIs provided by the Rust standard library areinterior-unsafe functions, such as Arc, Rc, Cell, RefCell,Mutex, and RwLock. Section 4.3 presents our analysis of interior unsafe usages in the Rust standard library.1 struct TestCell { value: i32, }2 unsafe impl Sync for TestCell{}3 impl TestCell {4fn set(&self, i: i32) {let p &self.value as * const i32 as * mut i32;unsafe{*p i};5678 }}Figure 4. Sample code for (interior) unsafe.2.4that allow read-only aliasing. A value’s reference can alsobe mutable, allowing write access to the value, but there canonly be one mutable reference and no immutable referencesat any single time. After borrowing a value’s ownershipthrough mutable reference, the temporary owner has the exclusive write access to the value. In Figure 3, an immutablereference (r1) and a mutable reference (r2) are created atline 15 and line 16, respectively. The Rust compiler does notallow line 18, since it will make the lifetime of r1 end afterline 18, making r1 and r2 co-exist at line 16 and line 17.2.3Bug Detection in RustRust runtime detects and triggers a panic on certain typesof bugs, such as buffer overflow, division by zero and stackoverflow. Rust also provides more bug-detecting features inits debug build mode, including detection of double lock andinteger overflow. These dynamic detection mechanisms Rustprovides only capture a small number of issues.Rust uses LLVM [32] as its backend. Many static and dynamic bug detection techniques [4, 40, 88, 89] designed forC/C can also be applied to Rust. However, it is still valuable to build Rust-specific detectors, because Rust’s newlanguage features and libraries can cause new types of bugsas evidenced by our study.Researchers have designed a few bug detection techniquesfor Rust. Rust-clippy [64] is a static detector for memory bugsthat follow certain simple source-code patterns. It only covers a small amount of buggy patterns. Miri [43] is a dynamicmemory-bug detector that interprets and executes Rust’smid-level intermediate representation (MIR). Jung et al. proposed an alias model for Rust [27]. Based on this model, theybuilt a dynamic memory-bug detector that uses a stack to dynamically track all valid references/pointers to each memorylocation and reports potential undefined behavior and memory bugs when references are not used in a properly-nestedmanner. The two dynamic detectors rely on user-providedinputs that can trigger memory bugs. From our experiments,Miri also generates many false positives.These existing Rust bug detection tools all have their ownlimitations, and none of them targets concurrency bugs. Anempirical study on Rust bugs like this work is important. Itcan help future researchers and practitioners to build moreRust-specific detectors. In fact, we have built two detectorsbased on our findings in this study, both of which revealpreviously undiscovered bugs.Unsafe and Interior UnsafeRust’s safety rules are strict and its static compiler checkingfor the rules is conservative. Developers (especially low-levelsoftware developers) often need more flexibility in writingtheir code, and some desires to manage safety by themselves(see Section 4 for real examples). Rust allows programs to bypass its safety checking with the unsafe feature, denoted bythe keyword unsafe. A function can be marked as unsafe;a piece of code can be marked as unsafe; and a trait canbe marked as unsafe (Rust traits are similar to interfacesin traditional languages like Java). Code regions markedwith unsafe will bypass Rust’s compiler checks and be ableto perform five types of functionalities: dereferencing andmanipulating raw pointers, accessing and modifying mutable static variables (i.e., global variables), calling unsafefunctions, implementing unsafe traits, and accessing unionfields. Figure 4 shows the implementation of a simple struct,which implements

designers can use our study results and the concrete, action-able suggestions we made to improve Rust software devel-opment (better programming practices, better bug detection akes the following contributions. The irst empirical study on rea

Related Documents:

435. 438 530. 55 V 60 V. ACME Acme-PT (Partial Topping) B Buttress Thread BSPT British Std Pipe Thread ISO ISO Thread NPT National Pipe Thread NPTF National Pipe Thread (Dry Seal) AP Pittsburgh Acme RD API Round Thread ACMEST Stub Acme TR Trapezoidal Thread UN Unified Thread

a square thread with parallel major and minor diam-eters.28Reverse buttress thread shape is optimized for pull-out loads and has parallel major and minor diam-eters.31 The reverse buttress thread design has fewer threads and less thread depth. Square thread shape, called a power thread in engi-neering, reportedly provides an optimized surface area

roll threaded should have a UNR thread because thread rolling dies with rounded crests are now the standard method for manufacturing most threads. UNJ Threads UNJ thread is a thread form having root radius limits of 0.150 to 0.180 times the thread pitch. With these enlarged radii, minor diameters of external thread increase and intrude beyond .

3. Bobbin Thread Cutter 4. Spool Holder (Large) 5. Spool Pin 6. Bobbin Winding Thread Guide 7. Thread Guide Plate 8. Thread Take-Up Lever 9. Thread Tension Dial 10. Face Plate 11. Thread Cutter & Thread Holder 12. Needle Threader 13. Needle Plate 14. Hook Cover Plate 15. Extension Table (Accessory Box) 16

ESCUTCHEON CATALOG LARSEN SUPPLY CO., INC. FAUCET REPAIR E-5 03-1713 Carded 5/8" - 18 Thread -- 9/16" - 20 Thread American Standard 03-1717 Carded 5/8" - 18 Thread -- 5/8" - 16 Thread Kohler 03-1719 Carded 5/8" - 18 Thread -- 9/16" - 18 Thread Crane BRASS REDUCING NIPPLES (USE TO PUT PRICE PFISTER ESCUTCHEONS ON OTHER MANUFACTURER'S VALVES)

ANSI/B1.20.1 ISO 7 Size EN 837-1 G ⅛ B, male thread G ¼ B, male thread M10 x 1, male thread ANSI/B1.20.1 ⅛ NPT, male thread ¼ NPT, male thread ISO 7 R ⅛, male thread R ¼, male thread Materials (wetted) Measuring element Copper alloy Process connection with lower measuring flangeCopper alloy

part. All GSG Thread Plug Gages are manufactured to ASME/ANSI B1.2 or B1.16M. All gages to ASME/ANSI B1.2 or B1.16M. All gages are made of oil hardened Tool Steel. TAPERLOCK THREAD PLUGS REVERSIBLE THREAD PLUGS TRILOCK THREAD PLUGS Reversible style thread work plug gages are avail-style thread work plug gages are avail-

cutting and adjustment of the Acme thread. V-Thread, Sharp V-thread.— The sides of the thread form an angle of 60 degrees with each other. The top and bottom or root of this thread form are theoretically sharp, but in actual practice the thread is made with a slight flat, owing to the difficulty of producing a