{"action":"created","issue":{"url":"https://api.github.com/repos/rust-lang/rust/issues/21812","labels_url":"https://api.github.com/repos/rust-lang/rust/issues/21812/labels{/name}","comments_url":"https://api.github.com/repos/rust-lang/rust/issues/21812/comments","events_url":"https://api.github.com/repos/rust-lang/rust/issues/21812/events","html_url":"https://github.com/rust-lang/rust/issues/21812","id":56130553,"number":21812,"title":"Variadic argument extern functions accept non-repr(C) structs","user":{"login":"tsion","id":26806,"avatar_url":"https://avatars.githubusercontent.com/u/26806?v=3","gravatar_id":"","url":"https://api.github.com/users/tsion","html_url":"https://github.com/tsion","followers_url":"https://api.github.com/users/tsion/followers","following_url":"https://api.github.com/users/tsion/following{/other_user}","gists_url":"https://api.github.com/users/tsion/gists{/gist_id}","starred_url":"https://api.github.com/users/tsion/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tsion/subscriptions","organizations_url":"https://api.github.com/users/tsion/orgs","repos_url":"https://api.github.com/users/tsion/repos","events_url":"https://api.github.com/users/tsion/events{/privacy}","received_events_url":"https://api.github.com/users/tsion/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":5,"created_at":"2015-01-31T16:41:11Z","updated_at":"2015-01-31T22:44:37Z","closed_at":null,"body":"Rust wisely tells us to cast `u8` and `u16` to `c_int` (aka `u32`) and `f32` to `c_double` (aka `f64`) when passing values of these types in variadic argument lists, since C expects this kind of type promotion to occur for varargs. But one can get around this and accidentally break things by wrapping the type in a newtype-style struct:\r\n\r\n``` rust\r\nextern crate libc;\r\nuse libc::{c_char, c_int, c_double};\r\n\r\nextern {\r\n    fn printf(fmt: *const c_char, ...) -> c_int;\r\n}\r\n\r\nfn main() {\r\n    let x: f32 = 1f32 / 2f32;\r\n    let fmt = b\"1 / 2 = %f\\n\\0\".as_ptr() as *mut c_char;\r\n    struct Oops(f32);\r\n    unsafe {\r\n        // printf(fmt, x); // error: can't pass an f32 to variadic function, cast to c_double\r\n        printf(fmt, x as c_double); // 1 / 2 = 0.500000\r\n        printf(fmt, Oops(x));       // 1 / 2 = 0.000000 (wrong!)\r\n    }\r\n}\r\n```\r\n\r\nThe existing checks can be found [here](https://github.com/rust-lang/rust/blob/master/src/librustc_typeck/check/mod.rs#L2365-L2388).\r\n\r\nEDIT: Rust ought to warn about passing a non-`repr(C)` function as in this example."},"comment":{"url":"https://api.github.com/repos/rust-lang/rust/issues/comments/72340724","html_url":"https://github.com/rust-lang/rust/issues/21812#issuecomment-72340724","issue_url":"https://api.github.com/repos/rust-lang/rust/issues/21812","id":72340724,"user":{"login":"Diggsey","id":451321,"avatar_url":"https://avatars.githubusercontent.com/u/451321?v=3","gravatar_id":"","url":"https://api.github.com/users/Diggsey","html_url":"https://github.com/Diggsey","followers_url":"https://api.github.com/users/Diggsey/followers","following_url":"https://api.github.com/users/Diggsey/following{/other_user}","gists_url":"https://api.github.com/users/Diggsey/gists{/gist_id}","starred_url":"https://api.github.com/users/Diggsey/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Diggsey/subscriptions","organizations_url":"https://api.github.com/users/Diggsey/orgs","repos_url":"https://api.github.com/users/Diggsey/repos","events_url":"https://api.github.com/users/Diggsey/events{/privacy}","received_events_url":"https://api.github.com/users/Diggsey/received_events","type":"User","site_admin":false},"created_at":"2015-01-31T22:44:37Z","updated_at":"2015-01-31T22:44:37Z","body":"Basically, if you accidentally mismatch types to a variadic function, you can read invalid memory (for example, if you could pass in a i16 when the function is expecting an i32 then it will read an extra two bytes, potentially corrupting later parameters as well. That's why printf is very unsafe, and the automatic conversions to \"int\" are used in C to make it just a little safer - it also means you can safely pass in a \"i16\" to a format string \"%i\" without causing undefined behaviour."}}