Store and check shared memory ownership before resizing shared memory objects. The scenario we are resolving here is the following: FTL is already running and someone(/-thing) deletes all the shared memory objects. If another instance of FTL is then started, it doesn't know a previous process is running and creates new SHM objects for itself. Both processes can - in theory - run just fine without touching each other as they are both actually using *different* shared memory objects. You are right when you think this is bizarre. The reason is that the first one has the shared memory objects still mapped into memory, i.e., "deleting" them from disk only removes the visible file handles. The next FTL instance creates files with the very same name, however, they are also new and distict files, i.e., their memory will point elsewhere. Both instances can now run in parallel just fine *until* one of them needs to resize a shared memory objects. If one of the shared memory events now gets resized to a size larger than it was before BUT smaller than what the other FTL instance is expecting, the other instance will instantaneously crash with a SIGSEGV (Bus error).

This commit resolves this by storing the PID of the SHM object creator in the settings object.
Each FTL instance reloads this shared memory instance now before performing any potentially dangerous operation and checks if the shared memory files on disk are still owned by this process. If this is not the case, we are in serious trouble and exit immediately.
This should allow the second instance (you could call it the "rightful owner" of the current existing SHM objects) a fairly good chance to never even notice this and continue to operate just fine.

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER 2022-11-02 20:49:01 +00:00
parent fc303aeba5
commit 1d447acb5a
No known key found for this signature in database
GPG Key ID: 00135ACBD90B28DD
2 changed files with 58 additions and 0 deletions

View File

@ -28,6 +28,8 @@
#include "files.h"
// log_resource_shortage()
#include "database/message-table.h"
// check_running_FTL()
#include "procps.h"
/// The version of shared memory used
#define SHARED_MEMORY_VERSION 14
@ -104,6 +106,7 @@ static ShmSettings *shmSettings = NULL;
static int pagesize;
static unsigned int local_shm_counter = 0;
static pid_t shmem_pid = 0;
static size_t used_shmem = 0u;
static size_t get_optimal_object_size(const size_t objsize, const size_t minsize);
@ -128,6 +131,56 @@ static int get_dev_shm_usage(char buffer[64])
return percentage;
}
// Verify the PID stored during shared memory initialization is the same as ours
// (while we initialized the shared memory objects)
static void verify_shmem_pid(void)
{
const int fd = shm_open(SHARED_SETTINGS_NAME, O_RDWR, S_IRUSR | S_IWUSR);
if(fd == -1)
{
logg("FATAL: verify_shmem_pid(): Failed to open shared memory object \"%s\": %s",
SHARED_SETTINGS_NAME, strerror(errno));
exit(EXIT_FAILURE);
}
// Get new pointer
void *shm = mmap(NULL, shm_settings.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// Check for `mmap` error
if(shm == MAP_FAILED)
{
logg("FATAL: verify_shmem_pid(): Failed to map shared memory object \"%s\" (%i): %s",
SHARED_SETTINGS_NAME, fd, strerror(errno));
exit(EXIT_FAILURE);
}
// Close file descriptor after call to mmap
close(fd);
// Unmap old memory region
if(munmap(shm_settings.ptr, shm_settings.size) < 0)
{
logg("FATAL: verify_shmem_pid(): Failed to unmap shared memory object \"%s\" (%i): %s",
SHARED_SETTINGS_NAME, fd, strerror(errno));
exit(EXIT_FAILURE);
}
// Update pointer
shmSettings = shm_settings.ptr = shm;
// Compare the SHM's PID to the one we had when creating the SHM objects
if(shmSettings->pid == shmem_pid)
return;
// If we reach here, we are in serious trouble. Terminating with error
// code is the most sensible thing we can do at this point
logg("FATAL: Shared memory is owned by a different process (PID %d)",
shmSettings->pid);
check_running_FTL();
logg("Exiting now!");
exit(EXIT_FAILURE);
}
// chown_shmem() changes the file ownership of a given shared memory object
static bool chown_shmem(SharedMemory *sharedMemory, struct passwd *ent_pw)
{
@ -461,6 +514,7 @@ bool init_shmem()
shmSettings = (ShmSettings*)shm_settings.ptr;
shmSettings->version = SHARED_MEMORY_VERSION;
shmSettings->global_shm_counter = 0;
shmSettings->pid = shmem_pid = getpid();
/****************************** shared strings buffer ******************************/
// Try to create shared memory object
@ -867,6 +921,9 @@ static size_t get_optimal_object_size(const size_t objsize, const size_t minsize
// Enlarge shared memory to be able to hold at least one new record
void shm_ensure_size(void)
{
// Verify shared memory ownership
verify_shmem_pid();
if(counters->queries >= counters->queries_MAX-1)
{
// Have to reallocate shared memory

View File

@ -26,6 +26,7 @@ typedef struct {
typedef struct {
int version;
pid_t pid;
unsigned int global_shm_counter;
unsigned int next_str_pos;
} ShmSettings;