Skip to content

Commit 489edd5

Browse files
committed
drop support for PA v4
i was never quite satisfied with the way this was implemented and have long meant to remove it.
1 parent 8b7b11d commit 489edd5

File tree

28 files changed

+68
-202
lines changed

28 files changed

+68
-202
lines changed

COMPATIBILITY.md

+15-19
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ however is a different and more complicated matter.
3434
## Controlling Compatibility
3535

3636
The Rust crates provided by this project each have a set of Cargo feature flags relating to
37-
PulseAudio versions. The supported versions of PA range from version 4.0 onwards.
37+
PulseAudio versions. The supported versions of PA range from version 5.0 onwards.
3838

3939
The first thing to understand is that such feature flags are provided only when a version of PA
4040
includes changes to its API/ABI that require one, not for every PA version ever released. Hence
@@ -101,20 +101,19 @@ flags. This change in operation is hard coded. They do **not** (because it is no
101101
what version it is at runtime, they just work on the assumption that the program will never be run
102102
with an older PA version. Thus, if this is ever not the case, things can go very wrong.
103103

104-
As an example, and in fact the primary area of concern, PA versions 5.0 and 14.0 both added new
105-
members to the end of some of the introspection structs. If you enable the feature flags for these
106-
versions (or newer), this adapts the crates to PA version 5.0+ or 14.0+ mode respectively. You will
107-
thus see that these new members are available in the corresponding structs offered by the crates,
108-
both in the raw FFI structs and also the ones returned by the introspection functions that translate
109-
those into the more “Rust-ified” ones. The operation of the “translation” functions will have been
110-
adjusted to include translation of those additional struct members. The danger here is that if your
111-
program is run on a system with an older version of PA, then the structs it’s C API provides will
112-
not match up correctly to the FFI struct description, lacking those extra members. The translation
113-
function will not know this however and will read and process the extra memory after the end of the
114-
actual structs the C API provided, assuming that memory to hold valid data. What then happens is
115-
undefined behaviour. Possibly a crash, possibly not, possibly an opportunity that could be seized
116-
upon as a security vulnerability, especially considering that some of these members involve
117-
pointers.
104+
As an example, and in fact the primary area of concern, PA version 14.0 added new members to the end
105+
of some of the introspection structs. If you enable the feature flags for this version (or newer),
106+
this adapts the crates to PA version 14.0+ mode. You will thus see that these new members are
107+
available in the corresponding structs offered by the crates, both in the raw FFI structs and also
108+
the ones returned by the introspection functions that translate those into the more “Rust-ified”
109+
ones. The operation of the “translation” functions will have been adjusted to include translation of
110+
those additional struct members. The danger here is that if your program is run on a system with an
111+
older version of PA, then the structs it’s C API provides will not match up correctly to the FFI
112+
struct description, lacking those extra members. The translation function will not know this however
113+
and will read and process the extra memory after the end of the actual structs the C API provided,
114+
assuming that memory to hold valid data. What then happens is undefined behaviour. Possibly a crash,
115+
possibly not, possibly an opportunity that could be seized upon as a security vulnerability,
116+
especially considering that some of these members involve pointers.
118117

119118
Ideally a mechanism should be in place to properly prevent programs from loading at all unless the
120119
version of PA is new enough. Unfortunately, as I understand it, the system library versioning scheme
@@ -126,9 +125,6 @@ these crates, and needs further work/investigation.
126125

127126
Currently it is left to program authors to somehow not let their programs be used on older versions
128127
of PA than they were compiled for, and/or to make careful choices as to the use of version features.
129-
(PA version 5.0 is pretty old, so it is unlikely that programs built for 5.0+ will get run on 4.0;
130-
and PA version 14.0 is very new, so unlikely that programs will select the version 14.0 feature flag
131-
for quite a while. So hopefully this should not be a practical problem).
132128

133129
Some helper functions are provided in the `version` mod of the main binding crate to assist in
134130
checking at runtime the version of the available PA system library by processing the version string
@@ -166,7 +162,7 @@ Reducing the level to PA version 6.0+ (requires disabling the current default of
166162
libpulse-binding = { version = "2.0", default-features = false, features = "pa_v6" }
167163
```
168164

169-
Reducing the level to the absolute minimum supported (currently PA v4.0+):
165+
Reducing the level to the absolute minimum supported (currently PA v5.0+):
170166

171167
```toml
172168
libpulse-binding = { version = "2.0", default-features = false }

pulse-binding-mainloop-glib/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# unreleased]
2+
3+
* Dropped PA v4 support.
4+
15
# 2.27.1 (January 9th, 2023)
26

37
* Fixed broken build status badge in readme.

pulse-binding-mainloop-glib/Cargo.toml

+1-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ pa_v14 = ["pa_v13", "libpulse-binding/pa_v14", "libpulse-mainloop-glib-sys/pa_v1
2828
pa_v13 = ["pa_v12", "libpulse-binding/pa_v13", "libpulse-mainloop-glib-sys/pa_v13"]
2929
pa_v12 = ["pa_v8", "libpulse-binding/pa_v12", "libpulse-mainloop-glib-sys/pa_v12"]
3030
pa_v8 = ["pa_v6", "libpulse-binding/pa_v8", "libpulse-mainloop-glib-sys/pa_v8"]
31-
pa_v6 = ["pa_v5", "libpulse-binding/pa_v6", "libpulse-mainloop-glib-sys/pa_v6"]
32-
pa_v5 = [ "libpulse-binding/pa_v5", "libpulse-mainloop-glib-sys/pa_v5"]
31+
pa_v6 = [ "libpulse-binding/pa_v6", "libpulse-mainloop-glib-sys/pa_v6"]
3332

3433
[package.metadata.docs.rs]
3534
all-features = false

pulse-binding-simple/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# unreleased]
2+
3+
* Dropped PA v4 support.
4+
15
# 2.27.1 (January 9th, 2023)
26

37
* Fixed broken build status badge in readme.

pulse-binding-simple/Cargo.toml

+1-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ pa_v14 = ["pa_v13", "libpulse-binding/pa_v14", "libpulse-sys/pa_v14", "libpulse-
2727
pa_v13 = ["pa_v12", "libpulse-binding/pa_v13", "libpulse-sys/pa_v13", "libpulse-simple-sys/pa_v13"]
2828
pa_v12 = ["pa_v8", "libpulse-binding/pa_v12", "libpulse-sys/pa_v12", "libpulse-simple-sys/pa_v12"]
2929
pa_v8 = ["pa_v6", "libpulse-binding/pa_v8", "libpulse-sys/pa_v8", "libpulse-simple-sys/pa_v8"]
30-
pa_v6 = ["pa_v5", "libpulse-binding/pa_v6", "libpulse-sys/pa_v6", "libpulse-simple-sys/pa_v6"]
31-
pa_v5 = [ "libpulse-binding/pa_v5", "libpulse-sys/pa_v5", "libpulse-simple-sys/pa_v5"]
30+
pa_v6 = [ "libpulse-binding/pa_v6", "libpulse-sys/pa_v6", "libpulse-simple-sys/pa_v6"]
3231

3332
[package.metadata.docs.rs]
3433
all-features = false

pulse-binding/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# unreleased]
2+
3+
* Dropped PA v4 support.
4+
15
# 2.27.1 (January 9th, 2023)
26

37
* Fixed broken build status badge in readme.

pulse-binding/Cargo.toml

+1-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ pa_v14 = ["pa_v13", "libpulse-sys/pa_v14"]
3232
pa_v13 = ["pa_v12", "libpulse-sys/pa_v13"]
3333
pa_v12 = ["pa_v8", "libpulse-sys/pa_v12"]
3434
pa_v8 = ["pa_v6", "libpulse-sys/pa_v8"]
35-
pa_v6 = ["pa_v5", "libpulse-sys/pa_v6"]
36-
pa_v5 = [ "libpulse-sys/pa_v5"]
35+
pa_v6 = [ "libpulse-sys/pa_v6"]
3736

3837
[package.metadata.docs.rs]
3938
all-features = false

pulse-binding/src/context/introspect.rs

+10-101
Original file line numberDiff line numberDiff line change
@@ -195,10 +195,7 @@ use capi::pa_source_info as SourceInfoInternal;
195195
use capi::pa_server_info as ServerInfoInternal;
196196
use capi::pa_module_info as ModuleInfoInternal;
197197
use capi::pa_client_info as ClientInfoInternal;
198-
#[cfg(not(any(doc, feature = "pa_v5")))]
199-
use capi::pa_card_profile_info as CardProfileInfoInternal;
200-
#[cfg(any(doc, feature = "pa_v5"))]
201-
use capi::pa_card_profile_info2 as CardProfileInfo2Internal;
198+
use capi::pa_card_profile_info2 as CardProfileInfoInternal;
202199
use capi::pa_card_port_info as CardPortInfoInternal;
203200
use capi::pa_card_info as CardInfoInternal;
204201
use capi::pa_sink_input_info as SinkInputInfoInternal;
@@ -1455,35 +1452,16 @@ fn get_client_info_list_cb_proxy(_: *mut ContextInternal, i: *const ClientInfoIn
14551452
// Card info
14561453
////////////////////////////////////////////////////////////////////////////////////////////////////
14571454

1458-
/// Stores information about a specific profile of a card.
1459-
///
1460-
/// Please note that this structure can be extended as part of evolutionary API updates at any time
1461-
/// in any new release.
1462-
///
1463-
/// Replaced with `CardProfileInfo2` in PA version 5+.
1464-
#[derive(Debug)]
1465-
#[cfg(not(any(doc, feature = "pa_v5")))]
1466-
pub struct CardProfileInfo<'a> {
1467-
/// Name of this profile.
1468-
pub name: Option<Cow<'a, str>>,
1469-
/// Description of this profile.
1470-
pub description: Option<Cow<'a, str>>,
1471-
/// Number of sinks this profile would create.
1472-
pub n_sinks: u32,
1473-
/// Number of sources this profile would create.
1474-
pub n_sources: u32,
1475-
/// The higher this value is, the more useful this profile is as a default.
1476-
pub priority: u32,
1477-
}
1455+
/// Backwards compatable alias
1456+
#[deprecated = "Use the name CardProfileInfo instead"]
1457+
pub type CardProfileInfo2<'a> = CardProfileInfo<'a>;
14781458

14791459
/// Stores information about a specific profile of a card.
14801460
///
14811461
/// Please note that this structure can be extended as part of evolutionary API updates at any time
14821462
/// in any new release.
14831463
#[derive(Debug)]
1484-
#[cfg(any(doc, feature = "pa_v5"))]
1485-
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v5")))]
1486-
pub struct CardProfileInfo2<'a> {
1464+
pub struct CardProfileInfo<'a> {
14871465
/// Name of this profile.
14881466
pub name: Option<Cow<'a, str>>,
14891467
/// Description of this profile.
@@ -1501,7 +1479,6 @@ pub struct CardProfileInfo2<'a> {
15011479
pub available: bool,
15021480
}
15031481

1504-
#[cfg(not(any(doc, feature = "pa_v5")))]
15051482
impl<'a> CardProfileInfo<'a> {
15061483
fn new_from_raw(p: *const CardProfileInfoInternal) -> Self {
15071484
assert!(!p.is_null());
@@ -1519,29 +1496,6 @@ impl<'a> CardProfileInfo<'a> {
15191496
n_sinks: src.n_sinks,
15201497
n_sources: src.n_sources,
15211498
priority: src.priority,
1522-
}
1523-
}
1524-
}
1525-
}
1526-
1527-
#[cfg(any(doc, feature = "pa_v5"))]
1528-
impl<'a> CardProfileInfo2<'a> {
1529-
fn new_from_raw(p: *const CardProfileInfo2Internal) -> Self {
1530-
assert!(!p.is_null());
1531-
let src = unsafe { &*p };
1532-
unsafe {
1533-
CardProfileInfo2 {
1534-
name: match src.name.is_null() {
1535-
false => Some(CStr::from_ptr(src.name).to_string_lossy()),
1536-
true => None,
1537-
},
1538-
description: match src.description.is_null() {
1539-
false => Some(CStr::from_ptr(src.description).to_string_lossy()),
1540-
true => None,
1541-
},
1542-
n_sinks: src.n_sinks,
1543-
n_sources: src.n_sources,
1544-
priority: src.priority,
15451499
available: match src.available {
15461500
0 => false,
15471501
_ => true,
@@ -1573,12 +1527,7 @@ pub struct CardPortInfo<'a> {
15731527
/// active.
15741528
pub latency_offset: i64,
15751529
/// Set of available profiles.
1576-
#[cfg(not(any(doc, feature = "pa_v5")))]
15771530
pub profiles: Vec<CardProfileInfo<'a>>,
1578-
/// Set of available profiles.
1579-
#[cfg(any(doc, feature = "pa_v5"))]
1580-
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v5")))]
1581-
pub profiles: Vec<CardProfileInfo2<'a>>,
15821531
/// An indentifier for the group of ports that share their availability status with each other.
15831532
///
15841533
/// This is meant especially for handling cases where one 3.5 mm connector is used for
@@ -1612,24 +1561,12 @@ impl<'a> CardPortInfo<'a> {
16121561

16131562
let mut profiles_vec = Vec::with_capacity(src.n_profiles as usize);
16141563

1615-
#[cfg(not(any(doc, feature = "pa_v5")))]
1616-
assert!(src.n_profiles == 0 || !src.profiles.is_null());
1617-
#[cfg(not(any(doc, feature = "pa_v5")))]
1618-
for i in 0..src.n_profiles as isize {
1619-
let indexed_ptr = unsafe { (*src.profiles.offset(i)) as *mut CardProfileInfoInternal };
1620-
if !indexed_ptr.is_null() {
1621-
profiles_vec.push(CardProfileInfo::new_from_raw(indexed_ptr));
1622-
}
1623-
}
1624-
1625-
#[cfg(any(doc, feature = "pa_v5"))]
16261564
assert!(src.n_profiles == 0 || !src.profiles2.is_null());
1627-
#[cfg(any(doc, feature = "pa_v5"))]
16281565
for i in 0..src.n_profiles as isize {
16291566
let indexed_ptr =
1630-
unsafe { (*src.profiles2.offset(i)) as *mut CardProfileInfo2Internal };
1567+
unsafe { (*src.profiles2.offset(i)) as *mut CardProfileInfoInternal };
16311568
if !indexed_ptr.is_null() {
1632-
profiles_vec.push(CardProfileInfo2::new_from_raw(indexed_ptr));
1569+
profiles_vec.push(CardProfileInfo::new_from_raw(indexed_ptr));
16331570
}
16341571
}
16351572

@@ -1680,19 +1617,9 @@ pub struct CardInfo<'a> {
16801617
/// Set of ports.
16811618
pub ports: Vec<CardPortInfo<'a>>,
16821619
/// Set of available profiles.
1683-
#[cfg(not(any(doc, feature = "pa_v5")))]
16841620
pub profiles: Vec<CardProfileInfo<'a>>,
16851621
/// Pointer to active profile in the set, or `None`.
1686-
#[cfg(not(any(doc, feature = "pa_v5")))]
16871622
pub active_profile: Option<Box<CardProfileInfo<'a>>>,
1688-
/// Set of available profiles.
1689-
#[cfg(any(doc, feature = "pa_v5"))]
1690-
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v5")))]
1691-
pub profiles: Vec<CardProfileInfo2<'a>>,
1692-
/// Pointer to active profile in the set, or `None`.
1693-
#[cfg(any(doc, feature = "pa_v5"))]
1694-
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v5")))]
1695-
pub active_profile: Option<Box<CardProfileInfo2<'a>>>,
16961623
}
16971624

16981625
impl<'a> CardInfo<'a> {
@@ -1710,24 +1637,12 @@ impl<'a> CardInfo<'a> {
17101637
}
17111638
let mut profiles_vec = Vec::with_capacity(src.n_profiles as usize);
17121639

1713-
#[cfg(not(any(doc, feature = "pa_v5")))]
1714-
assert!(src.n_profiles == 0 || !src.profiles.is_null());
1715-
#[cfg(not(any(doc, feature = "pa_v5")))]
1716-
for i in 0..src.n_profiles as isize {
1717-
let indexed_ptr = unsafe { src.profiles.offset(i) as *mut CardProfileInfoInternal };
1718-
if !indexed_ptr.is_null() {
1719-
profiles_vec.push(CardProfileInfo::new_from_raw(indexed_ptr));
1720-
}
1721-
}
1722-
1723-
#[cfg(any(doc, feature = "pa_v5"))]
17241640
assert!(src.n_profiles == 0 || !src.profiles2.is_null());
1725-
#[cfg(any(doc, feature = "pa_v5"))]
17261641
for i in 0..src.n_profiles as isize {
17271642
let indexed_ptr =
1728-
unsafe { (*src.profiles2.offset(i)) as *mut CardProfileInfo2Internal };
1643+
unsafe { (*src.profiles2.offset(i)) as *mut CardProfileInfoInternal };
17291644
if !indexed_ptr.is_null() {
1730-
profiles_vec.push(CardProfileInfo2::new_from_raw(indexed_ptr));
1645+
profiles_vec.push(CardProfileInfo::new_from_raw(indexed_ptr));
17311646
}
17321647
}
17331648

@@ -1749,15 +1664,9 @@ impl<'a> CardInfo<'a> {
17491664
proplist: Proplist::from_raw_weak(src.proplist),
17501665
ports: ports_vec,
17511666
profiles: profiles_vec,
1752-
#[cfg(not(any(doc, feature = "pa_v5")))]
1753-
active_profile: match src.active_profile.is_null() {
1754-
true => None,
1755-
false => Some(Box::new(CardProfileInfo::new_from_raw(src.active_profile))),
1756-
},
1757-
#[cfg(any(doc, feature = "pa_v5"))]
17581667
active_profile: match src.active_profile2.is_null() {
17591668
true => None,
1760-
false => Some(Box::new(CardProfileInfo2::new_from_raw(src.active_profile2))),
1669+
false => Some(Box::new(CardProfileInfo::new_from_raw(src.active_profile2))),
17611670
},
17621671
}
17631672
}

pulse-binding/src/context/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -615,8 +615,6 @@ impl Context {
615615
/// cookie from a custom location. Applications don’t usually need to care about the cookie at
616616
/// all, but if it happens that you know what the authentication cookie is and your application
617617
/// needs to load it from a non-standard location, feel free to use this function.
618-
#[cfg(any(doc, feature = "pa_v5"))]
619-
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v5")))]
620618
pub fn load_cookie_from_file(&mut self, cookie_file_path: &str) -> Result<(), PAErr> {
621619
// Warning: New CStrings will be immediately freed if not bound to a variable, leading to
622620
// as_ptr() giving dangling pointers!

pulse-binding/src/mainloop/threaded.rs

-3
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,6 @@
389389
//! ```
390390
391391
use std::rc::Rc;
392-
#[cfg(any(doc, feature = "pa_v5"))]
393392
use std::ffi::CString;
394393
use crate::def;
395394
use crate::error::PAErr;
@@ -554,8 +553,6 @@ impl Mainloop {
554553
}
555554

556555
/// Sets the name of the thread.
557-
#[cfg(any(doc, feature = "pa_v5"))]
558-
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v5")))]
559556
pub fn set_name(&mut self, name: &str) {
560557
// Warning: New CStrings will be immediately freed if not bound to a variable, leading to
561558
// as_ptr() giving dangling pointers!

pulse-binding/src/sample.rs

-6
Original file line numberDiff line numberDiff line change
@@ -316,24 +316,18 @@ impl Spec {
316316
///
317317
/// Or in other words that the client library running on the end user system accepts it.
318318
#[inline]
319-
#[cfg(any(doc, feature = "pa_v5"))]
320-
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v5")))]
321319
pub fn format_is_valid(&self) -> bool {
322320
unsafe { capi::pa_sample_format_valid(self.format as u32) != 0 }
323321
}
324322

325323
/// Checks only if the rate is within the supported range.
326324
#[inline]
327-
#[cfg(any(doc, feature = "pa_v5"))]
328-
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v5")))]
329325
pub fn rate_is_valid(&self) -> bool {
330326
unsafe { capi::pa_sample_rate_valid(self.rate) != 0 }
331327
}
332328

333329
/// Checks only if the channel count is within the supported range.
334330
#[inline]
335-
#[cfg(any(doc, feature = "pa_v5"))]
336-
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v5")))]
337331
pub fn channels_are_valid(&self) -> bool {
338332
unsafe { capi::pa_channels_valid(self.channels) != 0 }
339333
}

pulse-binding/src/version.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
//! versions of the PA client system library, with feature flags adapting the crate to changes made
2323
//! in the API of newer PA versions.
2424
//!
25-
//! Note that the minimum supported version of PA is v4.0.
25+
//! Note that the minimum supported version of PA is v5.0.
2626
//!
2727
//! # Runtime checking
2828
//!

pulse-binding/src/volume.rs

-5
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@
6161
6262
use std::borrow::{Borrow, BorrowMut};
6363
use std::ffi::CStr;
64-
#[cfg(any(doc, feature = "pa_v5"))]
6564
use std::ptr::null;
6665
use crate::sample;
6766
use crate::channelmap::{Map, Position, PositionMask, POSITION_MASK_ALL};
@@ -359,8 +358,6 @@ impl Volume {
359358
///
360359
/// The volume is printed in several formats: the raw volume value, percentage, and if
361360
/// `print_db` is true, also the dB value.
362-
#[cfg(any(doc, feature = "pa_v5"))]
363-
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v5")))]
364361
pub fn print_verbose(&self, print_db: bool) -> String {
365362
const PRINT_VERBOSE_MAX: usize = capi::PA_VOLUME_SNPRINT_VERBOSE_MAX;
366363
let mut tmp = Vec::with_capacity(PRINT_VERBOSE_MAX);
@@ -821,8 +818,6 @@ impl ChannelVolumes {
821818
/// The volume for each channel is printed in several formats: the raw volume value,
822819
/// percentage, and if `print_db` is non-zero, also the dB value. If `map` is provided, the
823820
/// channel names will be printed.
824-
#[cfg(any(doc, feature = "pa_v5"))]
825-
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v5")))]
826821
pub fn print_verbose(&self, map: Option<&Map>, print_db: bool) -> String {
827822
const PRINT_VERBOSE_MAX: usize = capi::PA_CVOLUME_SNPRINT_VERBOSE_MAX;
828823

0 commit comments

Comments
 (0)