[PATCH 3/3] sqlite: fork safety (bug 736334)

Zac Medico <[email protected]>
Newsgroups gmane.linux.gentoo.portage.devel
Message-ID <[email protected]>
Use a separate connection instance for each pid, since
it is not safe to use a connection created in a parent
process.

See: https://www.sqlite.org/howtocorrupt.html
Bug: https://bugs.gentoo.org/736334
Signed-off-by: Zac Medico <[email protected]>
---
 lib/portage/cache/sqlite.py           |  9 +++++----
 lib/portage/tests/dbapi/test_auxdb.py | 13 +++++++++++--
 2 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/lib/portage/cache/sqlite.py b/lib/portage/cache/sqlite.py
index 0395dd516..36a4f049e 100644
--- a/lib/portage/cache/sqlite.py
+++ b/lib/portage/cache/sqlite.py
@@ -4,6 +4,7 @@
 import collections
 import re
 
+import portage
 from portage.cache import fs_template
 from portage.cache import cache_errors
 from portage import os
@@ -25,7 +26,7 @@ class database(fs_template.FsBased):
 	cache_bytes = 1024 * 1024 * 10
 
 	_connection_info_entry = collections.namedtuple('_connection_info_entry',
-		('connection', 'cursor'))
+		('connection', 'cursor', 'pid'))
 
 	def __init__(self, *args, **config):
 		super(database, self).__init__(*args, **config)
@@ -71,13 +72,13 @@ class database(fs_template.FsBased):
 
 	@property
 	def _db_cursor(self):
-		if self._db_connection_info is None:
+		if self._db_connection_info is None or self._db_connection_info.pid != portage.getpid():
 			self._db_init_connection()
 		return self._db_connection_info.cursor
 
 	@property
 	def _db_connection(self):
-		if self._db_connection_info is None:
+		if self._db_connection_info is None or self._db_connection_info.pid != portage.getpid():
 			self._db_init_connection()
 		return self._db_connection_info.connection
 
@@ -94,7 +95,7 @@ class database(fs_template.FsBased):
 			connection = self._db_module.connect(
 				database=_unicode_decode(self._dbpath), **connection_kwargs)
 			cursor = connection.cursor()
-			self._db_connection_info = self._connection_info_entry(connection, cursor)
+			self._db_connection_info = self._connection_info_entry(connection, cursor, portage.getpid())
 			self._db_cursor.execute("PRAGMA encoding = %s" % self._db_escape_string("UTF-8"))
 			if not self.readonly and not self._ensure_access(self._dbpath):
 				raise cache_errors.InitializationError(self.__class__, "can't ensure perms on %s" % self._dbpath)
diff --git a/lib/portage/tests/dbapi/test_auxdb.py b/lib/portage/tests/dbapi/test_auxdb.py
index 5c79357d7..7865c3564 100644
--- a/lib/portage/tests/dbapi/test_auxdb.py
+++ b/lib/portage/tests/dbapi/test_auxdb.py
@@ -4,7 +4,8 @@
 from portage.tests import TestCase
 from portage.tests.resolver.ResolverPlayground import ResolverPlayground
 from portage.util.futures import asyncio
-from portage.util.futures.compat_coroutine import coroutine
+from portage.util.futures.compat_coroutine import coroutine, coroutine_return
+from portage.util.futures.executor.fork import ForkExecutor
 
 
 class AuxdbTestCase(TestCase):
@@ -61,8 +62,14 @@ class AuxdbTestCase(TestCase):
 
 		portdb = playground.trees[playground.eroot]["porttree"].dbapi
 
+		def test_func():
+			return asyncio._wrap_loop().run_until_complete(self._test_mod_async(
+				ebuilds, ebuild_inherited, eclass_defined_phases, eclass_depend, portdb))
+
+		self.assertTrue(test_func())
+
 		loop = asyncio._wrap_loop()
-		loop.run_until_complete(self._test_mod_async(ebuilds, ebuild_inherited, eclass_defined_phases, eclass_depend, portdb))
+		self.assertTrue(loop.run_until_complete(loop.run_in_executor(ForkExecutor(), test_func)))
 
 	@coroutine
 	def _test_mod_async(self, ebuilds, ebuild_inherited, eclass_defined_phases, eclass_depend, portdb):
@@ -73,3 +80,5 @@ class AuxdbTestCase(TestCase):
 			self.assertEqual(depend, eclass_depend)
 			self.assertEqual(eapi, metadata['EAPI'])
 			self.assertEqual(frozenset(inherited.split()), ebuild_inherited)
+
+		coroutine_return(True)
-- 
2.25.3
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.