Skip to main content

glycin_utils/
instruction_handler.rs

1// Copyright (c) 2024 GNOME Foundation Inc.
2
3use std::ffi::{c_int, c_void};
4use std::os::fd::FromRawFd;
5use std::os::unix::net::UnixStream;
6use std::sync::Mutex;
7
8use nix::libc::{c_uint, siginfo_t};
9
10use crate::dbus_editor_api::{Editor, EditorImplementation, VoidEditorImplementation};
11use crate::dbus_loader_api::{Loader, LoaderImplementation};
12
13pub struct DbusServer {
14    _dbus_connection: zbus::Connection,
15}
16
17impl DbusServer {
18    pub fn spawn_loader<L: LoaderImplementation>(description: String) {
19        futures_lite::future::block_on(async move {
20            let _connection = Self::connect::<L, VoidEditorImplementation>(description).await;
21            std::future::pending::<()>().await;
22        })
23    }
24
25    pub fn spawn_loader_editor<L: LoaderImplementation, E: EditorImplementation>(
26        description: String,
27    ) {
28        futures_lite::future::block_on(async move {
29            let _connection = Self::connect::<L, E>(description).await;
30            std::future::pending::<()>().await;
31        })
32    }
33
34    async fn connect<L: LoaderImplementation, E: EditorImplementation>(
35        description: String,
36    ) -> Self {
37        env_logger::builder().format_timestamp_millis().init();
38
39        log::info!("Loader {description} startup");
40
41        let mut dbus_fd_str = None;
42        let mut args = std::env::args().skip(1);
43        while let Some(arg) = args.next() {
44            match arg.as_str() {
45                "--dbus-fd" => {
46                    dbus_fd_str = args.next();
47                }
48
49                _ => {
50                    log::warn!("Stopping command line parsing at unknown argument: {arg:?}");
51                    break;
52                }
53            }
54        }
55
56        let Some(dbus_fd_str) = dbus_fd_str else {
57            log::error!("FD that facilitates the D-Bus connection not specified via --dbus-fd");
58            std::process::exit(2);
59        };
60
61        let Ok(dbus_fd) = dbus_fd_str.parse::<i32>() else {
62            log::error!("FD specified via --dbus-fd is not a valid number: {dbus_fd_str:?}",);
63            std::process::exit(2);
64        };
65
66        log::debug!("Creating zbus connection to glycin");
67
68        let unix_stream: UnixStream = unsafe { UnixStream::from_raw_fd(dbus_fd) };
69
70        #[cfg(feature = "tokio")]
71        let unix_stream =
72            tokio::net::UnixStream::from_std(unix_stream).expect("wrapping unix stream works");
73
74        let mut dbus_connection_builder = zbus::connection::Builder::unix_stream(unix_stream)
75            .p2p()
76            .auth_mechanism(zbus::AuthMechanism::Anonymous);
77
78        let loader_instruction_handler = Loader::<L> {
79            image_id: Mutex::new(1),
80            loader: Default::default(),
81        };
82
83        dbus_connection_builder = dbus_connection_builder
84            .serve_at("/org/gnome/glycin", loader_instruction_handler)
85            .expect("Failed to setup loader handler");
86
87        if E::USEABLE {
88            let editor_instruction_handler = Editor::<E> {
89                image_id: Mutex::new(1),
90                editor: Default::default(),
91            };
92            dbus_connection_builder = dbus_connection_builder
93                .serve_at("/org/gnome/glycin", editor_instruction_handler)
94                .expect("Failed to setup editor handler");
95        }
96
97        let dbus_connection = dbus_connection_builder
98            .build()
99            .await
100            .expect("Failed to create private DBus connection");
101
102        log::debug!("D-Bus connection to glycin created");
103        DbusServer {
104            _dbus_connection: dbus_connection,
105        }
106    }
107}
108
109#[allow(non_camel_case_types)]
110extern "C" fn sigsys_handler(_: c_int, info: *mut siginfo_t, _: *mut c_void) {
111    // Reimplement siginfo_t since the libc crate doesn't support _sigsys
112    // information
113    #[repr(C)]
114    struct siginfo_t {
115        si_signo: c_int,
116        si_errno: c_int,
117        si_code: c_int,
118        _sifields: _sigsys,
119    }
120
121    #[repr(C)]
122    struct _sigsys {
123        _call_addr: *const c_void,
124        _syscall: c_int,
125        _arch: c_uint,
126    }
127
128    let info: *mut siginfo_t = info.cast();
129    let syscall = unsafe { info.as_ref().unwrap()._sifields._syscall };
130
131    let name = libseccomp::ScmpSyscall::from(syscall).get_name().ok();
132
133    libc_eprint("glycin sandbox: Blocked syscall used: ");
134    libc_eprint(&name.unwrap_or_else(|| String::from("Unknown Syscall")));
135    libc_eprint(" (");
136    libc_eprint(&syscall.to_string());
137    libc_eprint(")\n");
138
139    unsafe {
140        libc::exit(128 + libc::SIGSYS);
141    }
142}
143
144fn setup_sigsys_handler() {
145    let mut mask = nix::sys::signal::SigSet::empty();
146    mask.add(nix::sys::signal::Signal::SIGSYS);
147
148    let sigaction = nix::sys::signal::SigAction::new(
149        nix::sys::signal::SigHandler::SigAction(sigsys_handler),
150        nix::sys::signal::SaFlags::SA_SIGINFO,
151        mask,
152    );
153
154    unsafe {
155        if nix::sys::signal::sigaction(nix::sys::signal::Signal::SIGSYS, &sigaction).is_err() {
156            libc_eprint("glycin sandbox: Failed to init syscall failure signal handler");
157        }
158    };
159}
160
161#[allow(dead_code)]
162pub extern "C" fn pre_main() {
163    setup_sigsys_handler();
164}
165
166#[macro_export]
167macro_rules! init_main_loader {
168    ($loader:path) => {
169        /// Init handler for SIGSYS before main() to catch
170        #[cfg_attr(target_os = "linux", unsafe(link_section = ".ctors"))]
171        static __CTOR: extern "C" fn() = pre_main;
172
173        fn main() {
174            $crate::DbusServer::spawn_loader::<$loader>(format!(
175                "{} v{}",
176                env!("CARGO_PKG_NAME"),
177                env!("CARGO_PKG_VERSION")
178            ));
179        }
180    };
181}
182
183#[macro_export]
184macro_rules! init_main_loader_editor {
185    ($loader:path, $editor:path) => {
186        /// Init handler for SIGSYS before main() to catch
187        #[cfg_attr(target_os = "linux", unsafe(link_section = ".ctors"))]
188        static __CTOR: extern "C" fn() = pre_main;
189
190        fn main() {
191            $crate::DbusServer::spawn_loader_editor::<$loader, $editor>(format!(
192                "{} v{}",
193                env!("CARGO_PKG_NAME"),
194                env!("CARGO_PKG_VERSION")
195            ));
196        }
197    };
198}
199
200fn libc_eprint(s: &str) {
201    unsafe {
202        libc::write(
203            libc::STDERR_FILENO,
204            s.as_ptr() as *const libc::c_void,
205            s.len(),
206        );
207    }
208}