From 57b50658a2b306244047e686212dbe3ffce3a6e6 Mon Sep 17 00:00:00 2001 From: Trevor Slocum Date: Fri, 8 Apr 2022 14:55:02 -0700 Subject: [PATCH] Obtain an exclusive lock before writing to the database This prevents race conditions caused by multiple instances of TinyIB accessing the database simultaneously. Resolves #251. --- imgboard.php | 8 ++++++++ inc/defines.php | 1 + inc/functions.php | 13 +++++++++++++ 3 files changed, 22 insertions(+) diff --git a/imgboard.php b/imgboard.php index 4462a70..9879159 100644 --- a/imgboard.php +++ b/imgboard.php @@ -257,6 +257,8 @@ if (!$loggedin) { $redirect = true; // Check if the request is to make a post if (!isset($_GET['delete']) && !isset($_GET['manage']) && (isset($_POST['name']) || isset($_POST['email']) || isset($_POST['subject']) || isset($_POST['message']) || isset($_POST['file']) || isset($_POST['embed']) || isset($_POST['password']))) { + $lock = lockDatabase(); + if (TINYIB_DBMIGRATE) { fancyDie(__('Posting is currently disabled.
Please try again in a few moments.')); } @@ -600,6 +602,8 @@ if (!isset($_GET['delete']) && !isset($_GET['manage']) && (isset($_POST['name']) die(); // Check if the request is to report a post } elseif (isset($_GET['report']) && !isset($_GET['manage'])) { + $lock = lockDatabase(); + if (!TINYIB_REPORT) { fancyDie(__('Reporting is disabled.')); } @@ -681,6 +685,8 @@ EOF; fancyDie(__('Post reported.'), $go_back); // Check if the request is to delete a post and/or its associated image } elseif (isset($_GET['delete']) && !isset($_GET['manage'])) { + $lock = lockDatabase(); + if (!isset($_POST['delete'])) { fancyDie(__('Tick the box next to a post and click "Delete" to delete it.')); } @@ -721,6 +727,8 @@ EOF; $redirect = false; // Check if the request is to access the management area } elseif (isset($_GET['manage'])) { + $lock = lockDatabase(); + $text = ''; $onload = ''; $navbar = ' '; diff --git a/inc/defines.php b/inc/defines.php index a280493..2d1719d 100644 --- a/inc/defines.php +++ b/inc/defines.php @@ -6,6 +6,7 @@ if (!defined('TINYIB_BOARD')) { define('TINYIB_NEWTHREAD', '0'); define('TINYIB_INDEXPAGE', false); define('TINYIB_RESPAGE', true); +define('TINYIB_LOCKFILE', 'tinyib.lock'); define('TINYIB_WORDBREAK_IDENTIFIER', '@!@TINYIB_WORDBREAK@!@'); // Account roles diff --git a/inc/functions.php b/inc/functions.php index 95d7a87..8c760a6 100644 --- a/inc/functions.php +++ b/inc/functions.php @@ -11,6 +11,19 @@ if (!function_exists('array_column')) { } } +// lockDatabase obtains an exclusive lock to prevent race conditions when +// accessing the database. +function lockDatabase() { + if (TINYIB_LOCKFILE == '') { + return true; + } + $fp = fopen(TINYIB_LOCKFILE, 'c+'); + if (!flock($fp, LOCK_EX)) { + fancyDie('Failed to lock control file.'); + } + return $fp; +} + function hashData($data, $force = false) { global $bcrypt_salt; if (substr($data, 0, 4) == '$2y$' && !$force) {