Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix double-free and undefined behaviour in libstd::syn::unix::Thread::new #70597

Merged
merged 9 commits into from
Apr 3, 2020

Conversation

vakaras
Copy link
Contributor

@vakaras vakaras commented Mar 31, 2020

While working on concurrency support for Miri, I found that the libstd::syn::unix::Thread::new method has two potential problems: double-free and undefined behaviour.

Double-free could occur if the following events happened (credit for pointing this out goes to @RalfJung):

  1. The call to pthread_create successfully launched a new thread that executed to completion and deallocated p.
  2. The call to pthread_attr_destroy returned a non-zero value causing the assert_eq! to panic.
  3. Since mem::forget(p) was not yet executed, the destructor of p would be executed and cause a double-free.

As far as I understand, this code also violates the stacked-borrows aliasing rules and thus would result in undefined behaviour if these rules were adopted. The problem is that the ownership of p is passed to the newly created thread before the call to mem::forget. Since the call to mem::forget is still a call, it counts as a use of p and triggers UB.

This pull request changes the code to use mem::ManuallyDrop instead of mem::forget. As a consequence, in case of a panic, p would be potentially leaked, which while undesirable is probably better than double-free or undefined behaviour.

@rust-highfive
Copy link
Collaborator

r? @LukasKalbertodt

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Mar 31, 2020
Copy link
Member

@bjorn3 bjorn3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would also be nice if start_thread (not the thread_start below) accepted a Box<dyn FnOnce()> instead of a *mut u8.

src/libstd/sys/unix/thread.rs Outdated Show resolved Hide resolved
@RalfJung
Copy link
Member

credit for pointing this out goes to @ralfj

You meant me (@RalfJung), I think. ;)

@vakaras
Copy link
Contributor Author

vakaras commented Mar 31, 2020

credit for pointing this out goes to @ralfj

You meant me (@RalfJung), I think. ;)

Yes. 🤦

@rust-highfive
Copy link
Collaborator

The job mingw-check of your PR failed (pretty log, raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
2020-03-31T16:30:41.4590685Z ========================== Starting Command Output ===========================
2020-03-31T16:30:41.4593902Z [command]/bin/bash --noprofile --norc /home/vsts/work/_temp/26ef4fb0-cd82-4ea2-bf25-b3e91c70482f.sh
2020-03-31T16:30:41.4594143Z 
2020-03-31T16:30:41.4597593Z ##[section]Finishing: Disable git automatic line ending conversion
2020-03-31T16:30:41.4615966Z ##[section]Starting: Checkout rust-lang/rust@refs/pull/70597/merge to s
2020-03-31T16:30:41.4619072Z Task         : Get sources
2020-03-31T16:30:41.4619384Z Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
2020-03-31T16:30:41.4619640Z Version      : 1.0.0
2020-03-31T16:30:41.4619814Z Author       : Microsoft
---
2020-03-31T16:30:42.6660851Z ##[command]git remote add origin https://github.com/rust-lang/rust
2020-03-31T16:30:42.6666690Z ##[command]git config gc.auto 0
2020-03-31T16:30:42.6670306Z ##[command]git config --get-all http.https://github.com/rust-lang/rust.extraheader
2020-03-31T16:30:42.6673166Z ##[command]git config --get-all http.proxy
2020-03-31T16:30:42.6679766Z ##[command]git -c http.extraheader="AUTHORIZATION: basic ***" fetch --force --tags --prune --progress --no-recurse-submodules --depth=2 origin +refs/heads/*:refs/remotes/origin/* +refs/pull/70597/merge:refs/remotes/pull/70597/merge
---
2020-03-31T16:32:52.0161745Z  ---> 3fc1b512c57b
2020-03-31T16:32:52.0161939Z Step 6/7 : ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1
2020-03-31T16:32:52.0162444Z  ---> Using cache
2020-03-31T16:32:52.0162986Z  ---> 5ee4295733f4
2020-03-31T16:32:52.0165916Z Step 7/7 : ENV SCRIPT python2.7 ../x.py test src/tools/expand-yaml-anchors &&            python2.7 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu &&            python2.7 ../x.py build --stage 0 src/tools/build-manifest &&            python2.7 ../x.py test --stage 0 src/tools/compiletest &&            python2.7 ../x.py test src/tools/tidy &&            /scripts/validate-toolstate.sh
2020-03-31T16:32:52.0167541Z  ---> 3d07a0fa42fe
2020-03-31T16:32:52.0170713Z Successfully built 3d07a0fa42fe
2020-03-31T16:32:52.0195090Z Successfully tagged rust-ci:latest
2020-03-31T16:32:52.0468904Z Built container sha256:3d07a0fa42feb5754fc13bb2f7010ebe13e4b8b8cdbebed0c75d8da320c8c8ad
---
2020-03-31T16:34:53.4112016Z     Checking panic_abort v0.0.0 (/checkout/src/libpanic_abort)
2020-03-31T16:34:53.5805505Z     Checking backtrace v0.3.46
2020-03-31T16:34:54.2271213Z     Checking rustc-std-workspace-alloc v1.99.0 (/checkout/src/tools/rustc-std-workspace-alloc)
2020-03-31T16:34:54.2293375Z     Checking panic_unwind v0.0.0 (/checkout/src/libpanic_unwind)
2020-03-31T16:34:57.1887056Z error[E0606]: casting `&mut alloc_crate::boxed::Box<dyn core::ops::FnOnce()>` as `*mut libc::c_void` is invalid
2020-03-31T16:34:57.1888337Z    |
2020-03-31T16:34:57.1888337Z    |
2020-03-31T16:34:57.1895547Z 72 |             &mut *p as &mut Box<dyn FnOnce()> as *mut _,
2020-03-31T16:34:57.1897047Z 
2020-03-31T16:34:57.2336388Z error: aborting due to previous error
2020-03-31T16:34:57.2336649Z 
2020-03-31T16:34:57.2337424Z For more information about this error, try `rustc --explain E0606`.
---
2020-03-31T16:34:57.2553344Z   local time: Tue Mar 31 16:34:57 UTC 2020
2020-03-31T16:34:57.5214896Z   network time: Tue, 31 Mar 2020 16:34:57 GMT
2020-03-31T16:34:57.5215366Z == end clock drift check ==
2020-03-31T16:34:58.7530913Z 
2020-03-31T16:34:58.7608659Z ##[error]Bash exited with code '1'.
2020-03-31T16:34:58.7663954Z ##[section]Finishing: Run build
2020-03-31T16:34:58.7715373Z ##[section]Starting: Checkout rust-lang/rust@refs/pull/70597/merge to s
2020-03-31T16:34:58.7722060Z Task         : Get sources
2020-03-31T16:34:58.7722416Z Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
2020-03-31T16:34:58.7722766Z Version      : 1.0.0
2020-03-31T16:34:58.7722992Z Author       : Microsoft
2020-03-31T16:34:58.7722992Z Author       : Microsoft
2020-03-31T16:34:58.7723363Z Help         : [More Information](https://go.microsoft.com/fwlink/?LinkId=798199)
2020-03-31T16:34:58.7723822Z ==============================================================================
2020-03-31T16:34:59.1040829Z Cleaning any cached credential from repository: rust-lang/rust (GitHub)
2020-03-31T16:34:59.1091769Z ##[section]Finishing: Checkout rust-lang/rust@refs/pull/70597/merge to s
2020-03-31T16:34:59.1178576Z Cleaning up task key
2020-03-31T16:34:59.1179922Z Start cleaning up orphan processes.
2020-03-31T16:34:59.1375881Z Terminate orphan process: pid (6118) (python)
2020-03-31T16:34:59.1545308Z ##[section]Finishing: Finalize Job

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @rust-lang/infra. (Feature Requests)

@vakaras vakaras force-pushed the thread_new_double_free_bug_fix branch from d65ab72 to b9f4e85 Compare March 31, 2020 16:46
@vakaras
Copy link
Contributor Author

vakaras commented Mar 31, 2020

It would also be nice if start_thread (not the thread_start below) accepted a Box<dyn FnOnce()> instead of a *mut u8.

If I understand you correctly, the suggestion would be to move the cast to Box<dyn FnOnce()> from start_thread to its callers, right? As I can see, there is at least 5 of them, so I am not sure if this would make the code nicer.

@bjorn3
Copy link
Member

bjorn3 commented Mar 31, 2020

If I understand you correctly, the suggestion would be to move the cast to Box<dyn FnOnce()> from start_thread to its callers, right?

Yes. All of the callers cast to *mut u8 anyway, so they could just as well be honest that the pointer is actually a Box<Box<dyn FnOnce()>>. This will also make start_thread safer as it now only calls the unsafe stack_overflow::Handler::new(). That function is by the way implemented differently for each of the platforms that call start_thread, so maybe just inline start_thread at every call site? It is just two lines of code.

@vakaras
Copy link
Contributor Author

vakaras commented Mar 31, 2020

If I understand you correctly, the suggestion would be to move the cast to Box<dyn FnOnce()> from start_thread to its callers, right?

Yes. All of the callers cast to *mut u8 anyway, so they could just as well be honest that the pointer is actually a Box<Box<dyn FnOnce()>>. This will also make start_thread safer as it now only calls the unsafe stack_overflow::Handler::new(). That function is by the way implemented differently for each of the platforms that call start_thread, so maybe just inline start_thread at every call site? It is just two lines of code.

One thing that makes me hesitant to make this change is the feeling that there was a reason to structure code in this way. However, I agree that having at least main: *mut Box<dyn FnOnce()> instead of main: *mut u8 would feel much safer.

@bjorn3
Copy link
Member

bjorn3 commented Mar 31, 2020

@vakaras vakaras force-pushed the thread_new_double_free_bug_fix branch 2 times, most recently from 31047ff to 5cbca95 Compare March 31, 2020 20:00
@vakaras
Copy link
Contributor Author

vakaras commented Mar 31, 2020

start_thread used to contain more code: https://github.com/rust-lang/rust/blob/13f302d0c5dd3a88426da53ba07cdbe16459635b/src/libstd/sys/common/thread.rs

OK, I am convinced. I have updated the PR.

While looking into this I noticed that I missed that this file is unix specific and that code for other architectures also has the issue with mem::forget. I have update the PR to include the fixes for that code, too.

@rust-highfive
Copy link
Collaborator

The job mingw-check of your PR failed (pretty log, raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
2020-03-31T20:00:59.1700188Z ========================== Starting Command Output ===========================
2020-03-31T20:00:59.1718684Z [command]/bin/bash --noprofile --norc /home/vsts/work/_temp/dfb719b5-cfbd-4796-a2d8-5777d6f09130.sh
2020-03-31T20:00:59.1967148Z 
2020-03-31T20:00:59.2026076Z ##[section]Finishing: Disable git automatic line ending conversion
2020-03-31T20:00:59.2046618Z ##[section]Starting: Checkout rust-lang/rust@refs/pull/70597/merge to s
2020-03-31T20:00:59.2050188Z Task         : Get sources
2020-03-31T20:00:59.2050498Z Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
2020-03-31T20:00:59.2050827Z Version      : 1.0.0
2020-03-31T20:00:59.2051032Z Author       : Microsoft
---
2020-03-31T20:01:00.4351359Z ##[command]git remote add origin https://github.com/rust-lang/rust
2020-03-31T20:01:00.4359974Z ##[command]git config gc.auto 0
2020-03-31T20:01:00.4364595Z ##[command]git config --get-all http.https://github.com/rust-lang/rust.extraheader
2020-03-31T20:01:00.4369341Z ##[command]git config --get-all http.proxy
2020-03-31T20:01:00.4377780Z ##[command]git -c http.extraheader="AUTHORIZATION: basic ***" fetch --force --tags --prune --progress --no-recurse-submodules --depth=2 origin +refs/heads/*:refs/remotes/origin/* +refs/pull/70597/merge:refs/remotes/pull/70597/merge
---
2020-03-31T20:03:17.9427690Z  ---> 3fc1b512c57b
2020-03-31T20:03:17.9428046Z Step 6/7 : ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1
2020-03-31T20:03:17.9466073Z  ---> Using cache
2020-03-31T20:03:17.9467742Z  ---> 5ee4295733f4
2020-03-31T20:03:17.9469390Z Step 7/7 : ENV SCRIPT python2.7 ../x.py test src/tools/expand-yaml-anchors &&            python2.7 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu &&            python2.7 ../x.py build --stage 0 src/tools/build-manifest &&            python2.7 ../x.py test --stage 0 src/tools/compiletest &&            python2.7 ../x.py test src/tools/tidy &&            /scripts/validate-toolstate.sh
2020-03-31T20:03:17.9470824Z  ---> 3d07a0fa42fe
2020-03-31T20:03:17.9522498Z Successfully built 3d07a0fa42fe
2020-03-31T20:03:17.9560202Z Successfully tagged rust-ci:latest
2020-03-31T20:03:17.9823892Z Built container sha256:3d07a0fa42feb5754fc13bb2f7010ebe13e4b8b8cdbebed0c75d8da320c8c8ad
---
2020-03-31T20:07:00.7678046Z     Checking rustc_feature v0.0.0 (/checkout/src/librustc_feature)
2020-03-31T20:07:00.8039895Z     Checking fmt_macros v0.0.0 (/checkout/src/libfmt_macros)
2020-03-31T20:07:00.9885590Z     Checking rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
2020-03-31T20:07:01.1551727Z     Checking rustc_hir v0.0.0 (/checkout/src/librustc_hir)
2020-03-31T20:07:01.5690308Z     Checking rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
2020-03-31T20:07:03.7683051Z     Checking rustc_attr v0.0.0 (/checkout/src/librustc_attr)
2020-03-31T20:07:04.2206219Z     Checking rustc_parse v0.0.0 (/checkout/src/librustc_parse)
2020-03-31T20:07:06.1773683Z     Checking rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
2020-03-31T20:07:06.6066138Z     Checking rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
2020-03-31T20:08:50.4546073Z configure: build.locked-deps    := True
2020-03-31T20:08:50.4546386Z configure: llvm.ccache          := sccache
2020-03-31T20:08:50.4546865Z configure: build.cargo-native-static := True
2020-03-31T20:08:50.4547335Z configure: dist.missing-tools   := True
2020-03-31T20:08:50.4547960Z configure: build.configure-args := ['--enable-sccache', '--disable-manage-submodu ...
2020-03-31T20:08:50.4548525Z configure: writing `config.toml` in current directory
2020-03-31T20:08:50.4548767Z configure: 
2020-03-31T20:08:50.4549183Z configure: run `python /checkout/x.py --help`
2020-03-31T20:08:50.4549411Z configure: 
---
2020-03-31T20:10:10.7188385Z Hugepagesize:       2048 kB
2020-03-31T20:10:10.7188611Z DirectMap4k:      135104 kB
2020-03-31T20:10:10.7188837Z DirectMap2M:     4059136 kB
2020-03-31T20:10:10.7189078Z DirectMap1G:     5242880 kB
2020-03-31T20:10:10.7189965Z + python2.7 ../x.py test src/tools/expand-yaml-anchors
2020-03-31T20:10:12.0598992Z Ensuring the YAML anchors in the GitHub Actions config were expanded
2020-03-31T20:10:12.0598992Z Ensuring the YAML anchors in the GitHub Actions config were expanded
2020-03-31T20:10:12.0600626Z Building stage0 tool expand-yaml-anchors (x86_64-unknown-linux-gnu)
2020-03-31T20:10:12.2956147Z    Compiling unicode-xid v0.2.0
2020-03-31T20:10:12.4199588Z    Compiling syn v1.0.11
2020-03-31T20:10:13.2243792Z    Compiling linked-hash-map v0.5.2
2020-03-31T20:10:13.3014915Z    Compiling lazy_static v1.4.0
2020-03-31T20:10:13.3014915Z    Compiling lazy_static v1.4.0
2020-03-31T20:10:13.4483036Z    Compiling yaml-rust v0.4.3
2020-03-31T20:10:17.6856072Z    Compiling quote v1.0.2
2020-03-31T20:10:31.5402343Z    Compiling thiserror-impl v1.0.5
2020-03-31T20:10:36.0403954Z    Compiling thiserror v1.0.5
2020-03-31T20:10:36.1019646Z    Compiling yaml-merge-keys v0.4.0
2020-03-31T20:10:37.2407655Z    Compiling expand-yaml-anchors v0.1.0 (/checkout/src/tools/expand-yaml-anchors)
2020-03-31T20:10:40.3249429Z Build completed successfully in 0:00:29
2020-03-31T20:10:40.3286307Z + python2.7 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu
2020-03-31T20:10:40.5712141Z     Finished dev [unoptimized] target(s) in 0.18s
2020-03-31T20:10:41.6630173Z Checking rustdoc artifacts (x86_64-unknown-linux-gnu -> i686-pc-windows-gnu)
---
2020-03-31T20:12:41.6211625Z     Checking rustc_feature v0.0.0 (/checkout/src/librustc_feature)
2020-03-31T20:12:41.7385756Z     Checking fmt_macros v0.0.0 (/checkout/src/libfmt_macros)
2020-03-31T20:12:41.9329737Z     Checking rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
2020-03-31T20:12:42.0628960Z     Checking rustc_hir v0.0.0 (/checkout/src/librustc_hir)
2020-03-31T20:12:42.5341787Z     Checking rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
2020-03-31T20:12:44.7312855Z     Checking rustc_attr v0.0.0 (/checkout/src/librustc_attr)
2020-03-31T20:12:45.1915115Z     Checking rustc_parse v0.0.0 (/checkout/src/librustc_parse)
2020-03-31T20:12:47.2231936Z     Checking rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
2020-03-31T20:12:47.7323771Z     Checking rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
2020-03-31T20:16:37.3777495Z skip untracked path cpu-usage.csv during rustfmt invocations
2020-03-31T20:16:37.3780695Z skip untracked path src/doc/book/ during rustfmt invocations
2020-03-31T20:16:37.3785318Z skip untracked path src/doc/rust-by-example/ during rustfmt invocations
2020-03-31T20:16:37.3788545Z skip untracked path src/llvm-project/ during rustfmt invocations
2020-03-31T20:16:42.9206921Z Diff in /checkout/src/libstd/sys/hermit/thread.rs at line 5:
2020-03-31T20:16:42.9207322Z  use crate::io;
2020-03-31T20:16:42.9207516Z  use crate::mem;
2020-03-31T20:16:42.9216093Z  use crate::sys::hermit::abi;
2020-03-31T20:16:42.9234445Z -use crate::sys::{stack_overflow};
2020-03-31T20:16:42.9234725Z +use crate::sys::stack_overflow;
2020-03-31T20:16:42.9235432Z  use core::u32;
2020-03-31T20:16:42.9236402Z  
2020-03-31T20:16:42.9236402Z  
2020-03-31T20:16:42.9237807Z Running `"/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/rustfmt" "--config-path" "/checkout" "--edition" "2018" "--unstable-features" "--skip-children" "--check" "/checkout/src/libstd/sys/hermit/thread.rs"` failed.
2020-03-31T20:16:42.9238884Z If you're running `tidy`, try again with `--bless` flag. Or, you just want to format code, run `./x.py fmt` instead.
2020-03-31T20:16:42.9267200Z Build completed unsuccessfully in 0:00:36
2020-03-31T20:16:42.9316561Z == clock drift check ==
2020-03-31T20:16:42.9331868Z   local time: Tue Mar 31 20:16:42 UTC 2020
2020-03-31T20:16:42.9544296Z   network time: Tue, 31 Mar 2020 20:16:42 GMT
2020-03-31T20:16:42.9544296Z   network time: Tue, 31 Mar 2020 20:16:42 GMT
2020-03-31T20:16:42.9547230Z == end clock drift check ==
2020-03-31T20:16:44.7238794Z 
2020-03-31T20:16:44.7303771Z ##[error]Bash exited with code '1'.
2020-03-31T20:16:44.7316754Z ##[section]Finishing: Run build
2020-03-31T20:16:44.7364408Z ##[section]Starting: Checkout rust-lang/rust@refs/pull/70597/merge to s
2020-03-31T20:16:44.7369192Z Task         : Get sources
2020-03-31T20:16:44.7369549Z Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
2020-03-31T20:16:44.7369872Z Version      : 1.0.0
2020-03-31T20:16:44.7370116Z Author       : Microsoft
2020-03-31T20:16:44.7370116Z Author       : Microsoft
2020-03-31T20:16:44.7370483Z Help         : [More Information](https://go.microsoft.com/fwlink/?LinkId=798199)
2020-03-31T20:16:44.7370912Z ==============================================================================
2020-03-31T20:16:45.0669388Z Cleaning any cached credential from repository: rust-lang/rust (GitHub)
2020-03-31T20:16:45.0715157Z ##[section]Finishing: Checkout rust-lang/rust@refs/pull/70597/merge to s
2020-03-31T20:16:45.0807660Z Cleaning up task key
2020-03-31T20:16:45.0808729Z Start cleaning up orphan processes.
2020-03-31T20:16:45.0984032Z Terminate orphan process: pid (8193) (python)
2020-03-31T20:16:45.1128702Z ##[section]Finishing: Finalize Job

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @rust-lang/infra. (Feature Requests)

@vakaras vakaras force-pushed the thread_new_double_free_bug_fix branch from 5cbca95 to 753bc7d Compare March 31, 2020 22:15
@Amanieu
Copy link
Member

Amanieu commented Mar 31, 2020

This code would be much clearer if it used Box::into_raw, and converted the pointer back to a Box if an error occurs.

@vakaras vakaras force-pushed the thread_new_double_free_bug_fix branch from bb4bc63 to 5382347 Compare April 1, 2020 01:02
@vakaras
Copy link
Contributor Author

vakaras commented Apr 1, 2020

This code would be much clearer if it used Box::into_raw, and converted the pointer back to a Box if an error occurs.

Thanks for the suggestion! It really looks much cleaner now.

assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);

return if ret != 0 {
// The thread failed to start and as a result p was not consumed. Therefore, it is
// safe to reconstruct the box so that it gets deallocated.
let _ = Box::from_raw(p);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let _ = Box::from_raw(p);
drop(Box::from_raw(p));

IMO that is more clear (same for the other places).

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Apr 2, 2020
Centril added a commit to Centril/rust that referenced this pull request Apr 2, 2020
…fix, r=Amanieu,RalfJung

Fix double-free and undefined behaviour in libstd::syn::unix::Thread::new

While working on concurrency support for Miri, I found that the `libstd::syn::unix::Thread::new` method has two potential problems: double-free and undefined behaviour.

**Double-free** could occur if the following events happened (credit for pointing this out goes to @RalfJung):

1.  The call to `pthread_create` successfully launched a new thread that executed to completion and deallocated `p`.
2.  The call to `pthread_attr_destroy` returned a non-zero value causing the `assert_eq!` to panic.
3.  Since `mem::forget(p)` was not yet executed, the destructor of `p` would be executed and cause a double-free.

As far as I understand, this code also violates the stacked-borrows aliasing rules and thus would result in **undefined behaviour** if these rules were adopted.  The problem is that the ownership of `p` is passed to the newly created thread before the call to `mem::forget`. Since the call to `mem::forget` is still a call, it counts as a use of `p` and triggers UB.

This pull request changes the code to use `mem::ManuallyDrop` instead of `mem::forget`. As a consequence, in case of a panic, `p` would be potentially leaked, which while undesirable is probably better than double-free or undefined behaviour.
@Centril
Copy link
Contributor

Centril commented Apr 2, 2020

Failed in #70711 (comment), @bors r-

@bors bors added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Apr 2, 2020
@vakaras
Copy link
Contributor Author

vakaras commented Apr 2, 2020

Does anyone know if there is a way for me to run ./x.py check on all platforms?

@Amanieu
Copy link
Member

Amanieu commented Apr 2, 2020

You can edit src/ci/github-actions/ci.yml to add more targets to the pr builder. Just make sure to revert the changes before r+.

@vakaras
Copy link
Contributor Author

vakaras commented Apr 2, 2020

You can edit src/ci/github-actions/ci.yml to add more targets to the pr builder. Just make sure to revert the changes before r+.

Thank you for the tip! I tried another trick: just create empty bash scripts for the missing tools about which ./x.py check complained.

I have checked at least one target for each stack overflow handler. Hopefully, this time it goes through.

@RalfJung
Copy link
Member

RalfJung commented Apr 3, 2020

@bors r=Amanieu,RalfJung

@bors
Copy link
Contributor

bors commented Apr 3, 2020

📌 Commit 53aa5a1 has been approved by Amanieu,RalfJung

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Apr 3, 2020
@RalfJung
Copy link
Member

RalfJung commented Apr 3, 2020

src/libstd/sys/windows/stack_overflow_uwp.rs, src/libstd/sys/hermit/stack_overflow.rs and src/libstd/sys/cloudabi/stack_overflow.rs also have dummy handlers, are those still used?

@vakaras
Copy link
Contributor Author

vakaras commented Apr 3, 2020

src/libstd/sys/windows/stack_overflow_uwp.rs, src/libstd/sys/hermit/stack_overflow.rs and src/libstd/sys/cloudabi/stack_overflow.rs also have dummy handlers, are those still used?

They are still used, but they not need to be used... Sorry for not realizing that.

@vakaras
Copy link
Contributor Author

vakaras commented Apr 3, 2020

@RalfJung I have deleted the handlers for hermit and cloudabi and also the code that uses them. The handler from stack_overflow_uwp.rs is imported into windows/mod.rs as stack_overflow and unconditionally used in windows/thread.rs.

@RalfJung
Copy link
Member

RalfJung commented Apr 3, 2020

Hm, yes that looks reasonable but I'll wait for @Amanieu to confirm.

@vakaras
Copy link
Contributor Author

vakaras commented Apr 3, 2020

Hm, yes that looks reasonable but I'll wait for @Amanieu to confirm.

Makes sense. By the way, if we are waiting for Amanieu, wouldn't it be better to r- until we get the confirmation?

@Amanieu
Copy link
Member

Amanieu commented Apr 3, 2020

@bors r+

@bors
Copy link
Contributor

bors commented Apr 3, 2020

📌 Commit d512b22 has been approved by Amanieu

@RalfJung
Copy link
Member

RalfJung commented Apr 3, 2020

wouldn't it be better to r- until we get the confirmation?

The PR got dequeued automatically the moment you pushed again. ;)

@vakaras
Copy link
Contributor Author

vakaras commented Apr 3, 2020

wouldn't it be better to r- until we get the confirmation?

The PR got dequeued automatically the moment you pushed again. ;)

I see. Thanks for the explanation!

bors added a commit to rust-lang-ci/rust that referenced this pull request Apr 3, 2020
Rollup of 9 pull requests

Successful merges:

 - rust-lang#69860 (Use associated numeric consts in documentation)
 - rust-lang#70576 (Update the description of the ticket to point at RFC 1721)
 - rust-lang#70597 (Fix double-free and undefined behaviour in libstd::syn::unix::Thread::new)
 - rust-lang#70640 (Hide `task_context` when lowering body)
 - rust-lang#70641 (Remove duplicated code in trait selection)
 - rust-lang#70707 (Remove unused graphviz emitter)
 - rust-lang#70720 (Place TLS initializers with relocations in .tdata)
 - rust-lang#70735 (Clean up E0502 explanation)
 - rust-lang#70741 (Add test for rust-lang#59023)

Failed merges:

r? @ghost
@bors bors merged commit 1ea8653 into rust-lang:master Apr 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants