Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Zac Medico <zmedico@g.o>
Subject: [gentoo-portage-dev] [PATCH 3/3] sqlite: fork safety (bug 736334)
Date: Sat, 08 Aug 2020 04:13:35
Message-Id: 20200808040857.77352-4-zmedico@gentoo.org
In Reply to: [gentoo-portage-dev] [PATCH 0/3] sqlite: fork safety (bug 736334) by Zac Medico
1 Use a separate connection instance for each pid, since
2 it is not safe to use a connection created in a parent
3 process.
4
5 See: https://www.sqlite.org/howtocorrupt.html
6 Bug: https://bugs.gentoo.org/736334
7 Signed-off-by: Zac Medico <zmedico@g.o>
8 ---
9 lib/portage/cache/sqlite.py | 9 +++++----
10 lib/portage/tests/dbapi/test_auxdb.py | 13 +++++++++++--
11 2 files changed, 16 insertions(+), 6 deletions(-)
12
13 diff --git a/lib/portage/cache/sqlite.py b/lib/portage/cache/sqlite.py
14 index 0395dd516..36a4f049e 100644
15 --- a/lib/portage/cache/sqlite.py
16 +++ b/lib/portage/cache/sqlite.py
17 @@ -4,6 +4,7 @@
18 import collections
19 import re
20
21 +import portage
22 from portage.cache import fs_template
23 from portage.cache import cache_errors
24 from portage import os
25 @@ -25,7 +26,7 @@ class database(fs_template.FsBased):
26 cache_bytes = 1024 * 1024 * 10
27
28 _connection_info_entry = collections.namedtuple('_connection_info_entry',
29 - ('connection', 'cursor'))
30 + ('connection', 'cursor', 'pid'))
31
32 def __init__(self, *args, **config):
33 super(database, self).__init__(*args, **config)
34 @@ -71,13 +72,13 @@ class database(fs_template.FsBased):
35
36 @property
37 def _db_cursor(self):
38 - if self._db_connection_info is None:
39 + if self._db_connection_info is None or self._db_connection_info.pid != portage.getpid():
40 self._db_init_connection()
41 return self._db_connection_info.cursor
42
43 @property
44 def _db_connection(self):
45 - if self._db_connection_info is None:
46 + if self._db_connection_info is None or self._db_connection_info.pid != portage.getpid():
47 self._db_init_connection()
48 return self._db_connection_info.connection
49
50 @@ -94,7 +95,7 @@ class database(fs_template.FsBased):
51 connection = self._db_module.connect(
52 database=_unicode_decode(self._dbpath), **connection_kwargs)
53 cursor = connection.cursor()
54 - self._db_connection_info = self._connection_info_entry(connection, cursor)
55 + self._db_connection_info = self._connection_info_entry(connection, cursor, portage.getpid())
56 self._db_cursor.execute("PRAGMA encoding = %s" % self._db_escape_string("UTF-8"))
57 if not self.readonly and not self._ensure_access(self._dbpath):
58 raise cache_errors.InitializationError(self.__class__, "can't ensure perms on %s" % self._dbpath)
59 diff --git a/lib/portage/tests/dbapi/test_auxdb.py b/lib/portage/tests/dbapi/test_auxdb.py
60 index 5c79357d7..7865c3564 100644
61 --- a/lib/portage/tests/dbapi/test_auxdb.py
62 +++ b/lib/portage/tests/dbapi/test_auxdb.py
63 @@ -4,7 +4,8 @@
64 from portage.tests import TestCase
65 from portage.tests.resolver.ResolverPlayground import ResolverPlayground
66 from portage.util.futures import asyncio
67 -from portage.util.futures.compat_coroutine import coroutine
68 +from portage.util.futures.compat_coroutine import coroutine, coroutine_return
69 +from portage.util.futures.executor.fork import ForkExecutor
70
71
72 class AuxdbTestCase(TestCase):
73 @@ -61,8 +62,14 @@ class AuxdbTestCase(TestCase):
74
75 portdb = playground.trees[playground.eroot]["porttree"].dbapi
76
77 + def test_func():
78 + return asyncio._wrap_loop().run_until_complete(self._test_mod_async(
79 + ebuilds, ebuild_inherited, eclass_defined_phases, eclass_depend, portdb))
80 +
81 + self.assertTrue(test_func())
82 +
83 loop = asyncio._wrap_loop()
84 - loop.run_until_complete(self._test_mod_async(ebuilds, ebuild_inherited, eclass_defined_phases, eclass_depend, portdb))
85 + self.assertTrue(loop.run_until_complete(loop.run_in_executor(ForkExecutor(), test_func)))
86
87 @coroutine
88 def _test_mod_async(self, ebuilds, ebuild_inherited, eclass_defined_phases, eclass_depend, portdb):
89 @@ -73,3 +80,5 @@ class AuxdbTestCase(TestCase):
90 self.assertEqual(depend, eclass_depend)
91 self.assertEqual(eapi, metadata['EAPI'])
92 self.assertEqual(frozenset(inherited.split()), ebuild_inherited)
93 +
94 + coroutine_return(True)
95 --
96 2.25.3