Submitted By: Xi Ruoyao Date: 2024-09-04 Initial Package Version: 1.90.5 Upstream Status: Committed Origin: Upstream (see From lines below for commit SHA) Description: Fix an out-of-bound write causing test failures and daemon crash/hang at runtime, and adapt the test suite for the recently added polkit integration. From b71996a526a73a18ae5e66ad6ce52c297a458df9 Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Wed, 28 Aug 2024 12:57:40 +0800 Subject: [PATCH] linux: integration-test: Add polkit test Test action is allowed and not allowed when calling EnableChargeThreshold dbus API. --- src/linux/integration-test.py | 162 +++++++++++++++++++++++++++++++++- 1 file changed, 159 insertions(+), 3 deletions(-) diff --git a/src/linux/integration-test.py b/src/linux/integration-test.py index 61dfa56..df754c7 100755 --- a/src/linux/integration-test.py +++ b/src/linux/integration-test.py @@ -157,6 +157,7 @@ class Tests(dbusmock.DBusTestCase): self.daemon = None self.bluez = None self.start_logind({'CanHybridSleep' : 'yes'}) + self.start_polkitd({}) @classmethod def stop_process(cls, proc, timeout=1): @@ -324,6 +325,11 @@ class Tests(dbusmock.DBusTestCase): parameters or {}) self.addCleanup(self.stop_process, self.bluez) + def start_polkitd(self, parameters=None): + self.polkit, self.polkit_obj = self.spawn_server_template('polkitd', + parameters or {}) + self.addCleanup(self.stop_process, self.polkit) + def assertEventually(self, condition, message=None, timeout=50, value=True): '''Assert that condition function eventually returns True. @@ -1014,6 +1020,10 @@ class Tests(dbusmock.DBusTestCase): def test_battery_charge_limit_multiple_batteries(self): '''Battery with charge limits with multiple batteries''' + if not self.polkit: + self.start_polkitd({}) + self.polkit_obj.SetAllowed(['org.freedesktop.UPower.enable-charging-limit']) + self.testbed.add_device('power_supply', 'BAT0', None, ['type', 'Battery', 'present', '1', @@ -1062,9 +1072,75 @@ class Tests(dbusmock.DBusTestCase): with open(f'/sys/class/power_supply/{battery_name}/charge_control_end_threshold') as fp: self.assertEqual(fp.read(), '80') + def test_battery_charge_limit_multiple_batteries_polkit_not_allowed(self): + '''Battery with charge limits with multiple batteries, but polkit isn't allowed''' + + if not self.polkit: + self.start_polkitd({}) + + self.testbed.add_device('power_supply', 'BAT0', None, + ['type', 'Battery', + 'present', '1', + 'status', 'unknown', + 'energy_full', '60000000', + 'energy_full_design', '80000000', + 'energy_now', '48000000', + 'voltage_now', '12000000', + 'charge_control_start_threshold', '0', + 'charge_control_end_threshold', '100', + ], []) + self.testbed.set_property("/sys/class/power_supply/BAT0", 'CHARGE_LIMIT', '70,80') + + self.testbed.add_device('power_supply', 'BAT1', None, + ['type', 'Battery', + 'present', '1', + 'status', 'unknown', + 'energy_full', '60000000', + 'energy_full_design', '80000000', + 'energy_now', '48000000', + 'voltage_now', '12000000', + 'charge_control_start_threshold', '0', + 'charge_control_end_threshold', '100', + ], []) + self.testbed.set_property("/sys/class/power_supply/BAT1", 'CHARGE_LIMIT', '70,80') + + self.start_daemon() + devs = self.proxy.EnumerateDevices() + self.assertEqual(len(devs), 2) + bat0_up = devs[0] + bat1_up = devs[0] + + for bat in [bat0_up, bat1_up]: + self.assertEqual(self.get_dbus_dev_property(bat, 'ChargeThresholdSupported'), True) + self.assertEqual(self.get_dbus_dev_property(bat, 'ChargeThresholdEnabled'), False) + self.assertEqual(self.get_dbus_dev_property(bat, 'ChargeStartThreshold'), 70) + self.assertEqual(self.get_dbus_dev_property(bat, 'ChargeEndThreshold'), 80) + + with self.assertRaises(Exception) as cm: + self.enable_charge_limits(bat0_up, True) + ex = cm.exception + self.assertIn("Operation is not allowed", str(ex)) + + with self.assertRaises(Exception) as cm: + self.enable_charge_limits(bat1_up, True) + ex = cm.exception + self.assertIn("Operation is not allowed", str(ex)) + + for bat in [bat0_up, bat1_up]: + self.assertEqual(self.get_dbus_dev_property(bat, 'ChargeThresholdEnabled'), False) + battery_name = bat.split('_')[-1] + with open(f'/sys/class/power_supply/{battery_name}/charge_control_start_threshold') as fp: + self.assertEqual(fp.read(), '0') + with open(f'/sys/class/power_supply/{battery_name}/charge_control_end_threshold') as fp: + self.assertEqual(fp.read(), '100') + def test_battery_charge_limit_supported(self): '''Battery with charge_control_start/end_threshold supported''' + if not self.polkit: + self.start_polkitd({}) + self.polkit_obj.SetAllowed(['org.freedesktop.UPower.enable-charging-limit']) + self.testbed.add_device('power_supply', 'BAT0', None, ['type', 'Battery', 'present', '1', @@ -1135,9 +1211,89 @@ class Tests(dbusmock.DBusTestCase): with open('/sys/class/power_supply/BAT0/charge_control_end_threshold') as fp: self.assertEqual(fp.read(), '100') + def test_battery_charge_limit_supported_polkit_not_allowed(self): + '''Battery with charge_control_start/end_threshold supported''' + + if not self.polkit: + self.start_polkitd({}) + + self.testbed.add_device('power_supply', 'BAT0', None, + ['type', 'Battery', + 'present', '1', + 'model_name', 'test', + 'serial_number', '12', + 'status', 'unknown', + 'energy_full', '60000000', + 'energy_full_design', '80000000', + 'energy_now', '48000000', + 'voltage_now', '12000000', + 'charge_control_start_threshold', '0', + 'charge_control_end_threshold', '100', + ], []) + self.testbed.set_property("/sys/class/power_supply/BAT0", 'CHARGE_LIMIT', '70,80') + + def start_daemon(charge_threshold_value=None): + upower_history_dir_override = tempfile.mkdtemp(prefix='upower-history-') + if charge_threshold_value is not None: + with open(os.path.join(upower_history_dir_override, f"charging-threshold-status") , 'w') as fp: + fp.write(charge_threshold_value) + + self.start_daemon(history_dir_override=upower_history_dir_override) + devs = self.proxy.EnumerateDevices() + self.assertEqual(len(devs), 1) + return devs[0] + + bat0_up = start_daemon() + self.assertEqual(self.get_dbus_dev_property(bat0_up, 'ChargeThresholdSupported'), True) + self.assertEqual(self.get_dbus_dev_property(bat0_up, 'ChargeThresholdEnabled'), False) + self.assertEqual(self.get_dbus_dev_property(bat0_up, 'ChargeStartThreshold'), 70) + self.assertEqual(self.get_dbus_dev_property(bat0_up, 'ChargeEndThreshold'), 80) + + with self.assertRaises(Exception) as cm: + self.enable_charge_limits(bat0_up, True) + ex = cm.exception + self.assertIn("Operation is not allowed", str(ex)) + + self.assertEqual(self.get_dbus_dev_property(bat0_up, 'ChargeThresholdEnabled'), False) + # charge limits enabled? + with open('/sys/class/power_supply/BAT0/charge_control_start_threshold') as fp: + self.assertEqual(fp.read(), '0') + with open('/sys/class/power_supply/BAT0/charge_control_end_threshold') as fp: + self.assertEqual(fp.read(), '100') + + with self.assertRaises(Exception) as cm: + self.enable_charge_limits(bat0_up, False) + ex = cm.exception + self.assertIn("Operation is not allowed", str(ex)) + + self.assertEqual(self.get_dbus_dev_property(bat0_up, 'ChargeThresholdEnabled'), False) + with open('/sys/class/power_supply/BAT0/charge_control_start_threshold') as fp: + self.assertEqual(fp.read(), '0') + with open('/sys/class/power_supply/BAT0/charge_control_end_threshold') as fp: + self.assertEqual(fp.read(), '100') + + self.stop_daemon() + + # On startup with threshold set + self.testbed.set_property("/sys/class/power_supply/BAT0", 'CHARGE_LIMIT', '90,100') + bat0_up = start_daemon(charge_threshold_value='1') + self.assertEqual(self.get_dbus_dev_property(bat0_up, 'ChargeThresholdSupported'), True) + self.assertEqual(self.get_dbus_dev_property(bat0_up, 'ChargeThresholdEnabled'), True) + self.assertEqual(self.get_dbus_dev_property(bat0_up, 'ChargeStartThreshold'), 90) + self.assertEqual(self.get_dbus_dev_property(bat0_up, 'ChargeEndThreshold'), 100) + + with open('/sys/class/power_supply/BAT0/charge_control_start_threshold') as fp: + self.assertEqual(fp.read(), '90') + with open('/sys/class/power_supply/BAT0/charge_control_end_threshold') as fp: + self.assertEqual(fp.read(), '100') + def test_battery_charge_threshold_unsupported(self): '''Battery with only end_threshold supported''' + if not self.polkit: + self.start_polkitd({}) + self.polkit_obj.SetAllowed(['org.freedesktop.UPower.enable-charging-limit']) + self.testbed.add_device('power_supply', 'BAT0', None, ['type', 'Battery', 'present', '1', @@ -1159,10 +1315,10 @@ class Tests(dbusmock.DBusTestCase): self.assertEqual(self.get_dbus_dev_property(bat0_up, 'ChargeThresholdSupported'), False) self.assertEqual(self.get_dbus_dev_property(bat0_up, 'ChargeThresholdEnabled'), False) - try: + with self.assertRaises(Exception) as cm: self.enable_charge_limits(bat0_up, True) - except Exception as err: - self.assertIn("setting battery charge thresholds", str(err)) + ex = cm.exception + self.assertIn("setting battery charge thresholds", str(ex)) self.stop_daemon() -- 2.46.0 From b4697dbc626ced1a456bcb4aba8dca2fe1efa901 Mon Sep 17 00:00:00 2001 From: "Jan Alexander Steffens (heftig)" Date: Sat, 31 Aug 2024 11:05:54 +0200 Subject: [PATCH] up-polkit: Add `G_ADD_PRIVATE (UpPolkit)` Without this, accesses to `UpPolkitPrivate` are actually out of bounds and writing to it will cause heap corruption. Fixes: https://gitlab.freedesktop.org/upower/upower/-/issues/281 --- src/up-polkit.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/up-polkit.c b/src/up-polkit.c index 0ede5a7..e0ba246 100644 --- a/src/up-polkit.c +++ b/src/up-polkit.c @@ -43,7 +43,8 @@ struct UpPolkitPrivate #endif }; -G_DEFINE_TYPE (UpPolkit, up_polkit, G_TYPE_OBJECT) +G_DEFINE_TYPE_WITH_CODE (UpPolkit, up_polkit, G_TYPE_OBJECT, + G_ADD_PRIVATE (UpPolkit)) static gpointer up_polkit_object = NULL; #ifdef HAVE_POLKIT -- 2.46.0