Submitted By:            Xi Ruoyao <xry111 at xry111 dot site>
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 <hpa@redhat.com>
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)" <heftig@archlinux.org>
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

