Skip to content

Commit

Permalink
🔧 Warn on <limit>, add BusConfig::limits()
Browse files Browse the repository at this point in the history
fixes dbus2#148
  • Loading branch information
jokeyrhyme committed Nov 19, 2024
1 parent 768bb5a commit 42bdae0
Showing 1 changed file with 115 additions and 1 deletion.
116 changes: 115 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{collections::HashSet, path::PathBuf};
use std::{collections::HashSet, path::PathBuf, time::Duration};

use anyhow::{Error, Result};
use quick_xml::{events::Event, Reader};
Expand Down Expand Up @@ -85,6 +85,9 @@ impl TryFrom<Document> for BusConfig {
}
Element::Fork => bc.fork = true,
Element::KeepUmask => bc.keep_umask = true,
Element::Limit => {
eprintln!("warning: busd does not implement `<limit>`");
}
Element::Listen(s) => {
bc.listen.insert(s);
}
Expand Down Expand Up @@ -121,6 +124,64 @@ impl TryFrom<Document> for BusConfig {
}

impl BusConfig {
pub fn limits(&self) -> Limits {
match self.r#type {
Some(Type::Session) => Limits {
/* For the session bus, override the default relatively-low limits
with essentially infinite limits, since the bus is just running
as the user anyway, using up bus resources is not something we need
to worry about. In some cases, we do set the limits lower than
"all available memory" if exceeding the limit is almost certainly a bug,
having the bus enforce a limit is nicer than a huge memory leak. But the
intent is that these limits should never be hit. */
// dbus-daemon / dbus-broker is limited to the highest positive number in i32,
// but we use u32 here, so we can pick the preferred 4GB memory limits
max_incoming_bytes: 4000000000,
max_incoming_unix_fds: 250000000,
max_outgoing_bytes: 4000000000,
max_outgoing_unix_fds: 250000000,
max_message_size: 4000000000,
// We do not override max_message_unix_fds here,
// since the in-kernel limit is also relatively low
max_message_unix_fds: 16,
service_start_timeout: Duration::from_millis(120000),
auth_timeout: Duration::from_millis(240000),
pending_fd_timeout: Duration::from_millis(150000),
max_completed_connections: 100000,
max_incomplete_connections: 10000,
max_connections_per_user: 100000,
max_pending_service_starts: 10000,
max_names_per_connection: 50000,
max_match_rules_per_connection: 50000,
max_replies_per_connection: 50000,
..Default::default()
},
Some(Type::System) => Limits {
max_incoming_bytes: 133169152,
max_incoming_unix_fds: 64,
max_outgoing_bytes: 133169152,
max_outgoing_unix_fds: 64,
max_message_size: 33554432,
max_message_unix_fds: 16,
service_start_timeout: Duration::from_millis(25000),
auth_timeout: Duration::from_millis(5000),
pending_fd_timeout: Duration::from_millis(150000),
max_completed_connections: 2048,
max_incomplete_connections: 64,
max_connections_per_user: 256,
max_pending_service_starts: 512,
max_names_per_connection: 512,
max_match_rules_per_connection: 512,
max_replies_per_connection: 128,
..Default::default()
},
None => {
// TODO: consider panicking or returning an Error or something
Limits::default()
}
}
}

pub fn parse(s: &str) -> Result<Self> {
// validate that our DOCTYPE and root element are correct
let mut reader = Reader::from_reader(s.as_bytes());
Expand Down Expand Up @@ -182,7 +243,10 @@ enum Element {
Auth(String),
Fork,
KeepUmask,
// TODO: support `<include ignore_missing=(yes|no) if_selinux_enabled=(yes|no) selinux_root_relative=(yes|no)>`
// TODO: support `<includedir>`
Listen(String),
Limit,
Pidfile(PathBuf),
Servicedir(PathBuf),
Servicehelper(PathBuf),
Expand All @@ -196,6 +260,44 @@ enum Element {
User(String),
}

#[derive(Clone, Debug, Default, PartialEq)]
pub struct Limits {
/// total size in bytes of messages incoming from a single connection
pub max_incoming_bytes: u32,
/// total number of unix fds of messages incoming from a single connection
pub max_incoming_unix_fds: u32,
/// total size in bytes of messages queued up for a single connection
pub max_outgoing_bytes: u32,
/// total number of unix fds of messages queued up for a single connection
pub max_outgoing_unix_fds: u32,
/// max size of a single message in bytes
pub max_message_size: u32,
/// max unix fds of a single message
pub max_message_unix_fds: u32,
/// time a started service has to connect
pub service_start_timeout: Duration,
/// time a connection is given to authenticate
pub auth_timeout: Duration,
/// time a fd is given to be transmitted to dbus-daemon before disconnecting the connection
pub pending_fd_timeout: Duration,
/// max number of authenticated connections
pub max_completed_connections: u32,
/// max number of unauthenticated connections
pub max_incomplete_connections: u32,
/// max number of completed connections from the same user (only enforced on Unix OSs)
pub max_connections_per_user: u32,
/// max number of service launches in progress at the same time
pub max_pending_service_starts: u32,
/// max number of names a single connection can own
pub max_names_per_connection: u32,
/// max number of match rules for a single connection
pub max_match_rules_per_connection: u32,
/// max number of pending method replies per connection (number of calls-in-progress)
pub max_replies_per_connection: u32,
/// time until a method call times out
pub reply_timeout: Duration,
}

#[derive(Clone, Debug, Deserialize, PartialEq)]
struct TypeElement {
#[serde(rename = "$text")]
Expand Down Expand Up @@ -298,6 +400,18 @@ mod tests {
assert_eq!(diff.count(), 0);
}

#[test]
fn bus_config_parse_with_limit_ok() {
let input = r#"<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<limit name="max_incoming_bytes">1000000000</limit>
</busconfig>
"#;

BusConfig::parse(input).expect("should parse XML input");
}

#[test]
fn bus_config_parse_with_listen_ok() {
let input = r#"<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
Expand Down

0 comments on commit 42bdae0

Please sign in to comment.