Skip to content

Commit 3c6ff46

Browse files
Replace pid with actual bitpattern
1 parent 9907a0b commit 3c6ff46

5 files changed

Lines changed: 87 additions & 25 deletions

File tree

‎score/launch_manager/src/daemon/src/osal/ipc_comms.hpp‎

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#define OSAL_IPC_COMMS_HPP_INCLUDED
1616

1717
#include <sys/mman.h>
18+
#include <cstdint>
1819
#include <memory>
1920
#include <utility>
2021

@@ -29,21 +30,30 @@ using IpcCommsP = std::shared_ptr<IpcCommsSync>;
2930

3031
/// @brief Structure for managing inter-process communication synchronization.
3132
/// The `IpcCommsSync` structure is designed to handle synchronization mechanisms required
32-
/// for inter-process communication. It uses a semaphore to signal kRunning and
33-
/// a process ID to identify the communicating process.
33+
/// for inter-process communication. It uses a semaphore to signal kRunning, preceded by a magic number and a
34+
/// layout version that allow the client to verify that the shared memory really was set up by Launch Manager.
3435
/// It is only created for processes that are expected to report kRunning.
3536
struct IpcCommsSync final
3637
{
38+
/// @brief Magic number used as a safeguard by application processes
39+
static constexpr std::uint32_t kMagic{0x4C4D5352U};
40+
41+
/// @brief Expected value of `version_`.
42+
static constexpr std::uint32_t kLayoutVersion{1U};
43+
44+
/// @brief Marks the shared memory as a comms object created by Launch Manager.
45+
/// It is the first member, so that checking it does not depend on the layout of the remaining members.
46+
/// Launch Manager writes it last, after all other members have been initialized.
47+
std::uint32_t magic_;
48+
49+
/// @brief Layout version of this structure, to detect a Launch Manager and client built from different versions.
50+
std::uint32_t version_;
51+
3752
/// @brief Semaphore used by the process to report kRunning.
3853
/// The communicating process posts this semaphore exactly once to signal that it has finished initialization.
3954
/// Launch Manager waits on it (and also posts it to wake the waiter up early if the process terminates).
4055
Semaphore send_sync_;
4156

42-
/// @brief Process ID of the communicating process.
43-
/// The `pid_` is written by the child process right after fork. The client checks it against its own pid.
44-
/// The `pid_` is used as an expected bit pattern on the client side.
45-
ProcessID pid_;
46-
4757
/// @brief Constant for the synchronization file descriptor.
4858
/// The `sync_fd` is a constant representing the file descriptor used for synchronization
4959
/// during communication. It is set to a value of 111 by default.
@@ -109,6 +119,9 @@ struct IpcCommsSync final
109119
return nullptr; // Releasing mapping unmaps the memory
110120
}
111121

122+
mapping->version_ = kLayoutVersion;
123+
mapping->magic_ = kMagic;
124+
112125
IpcCommsSync* const block = mapping.get();
113126
return IpcCommsP(block, IpcCommsOwnerDeletor{std::move(mapping)});
114127
}

‎score/launch_manager/src/daemon/src/process_group_manager/details/process_launcher.cpp‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ void handleComms(score::mw::lifecycle::internal::osal::ChildProcessConfig& param
8989
}
9090

9191
param.fd = dup2(param.fd, param.shared_block->sync_fd);
92-
param.shared_block->pid_ = getpid(); // Store pid for check at client end
9392

9493
// Flag FD_CLOEXEC is cleared to ensure that sync_fd remains open after the execve call.
9594
if (-1 == fcntl(IpcCommsSync::sync_fd, F_SETFD, 0))

‎score/launch_manager/src/daemon/src/process_group_manager/details/process_launcher_UT.cpp‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ class ProcessLauncherTest : public ::testing::Test
7171
std::memset(test_buffer.data(), 0, test_buffer.size());
7272
auto* sync = reinterpret_cast<IpcCommsSync*>(test_buffer.data());
7373

74-
sync->pid_ = 0;
7574
EXPECT_EQ(sync->send_sync_.init(0, false), OsalReturnType::kSuccess);
7675
// Real msync calls would treat this fake shared memory as invalid because it isn't page aligned.
7776
ON_CALL(*g_syscall_mock, msync(sync, _, _)).WillByDefault(Return(0));
@@ -397,12 +396,18 @@ TEST_F(SetupCommsTest, getCommsFails)
397396

398397
TEST_F(SetupCommsTest, initSemaphoresFails)
399398
{
400-
RecordProperty("Description", "Verify that if setting up the semaphores fails, startProcess fails");
399+
RecordProperty(
400+
"Description",
401+
"Verify that if setting up the semaphores fails, startProcess fails, the shared memory is not marked as a "
402+
"valid comms object and no semaphore is destroyed");
401403

402-
StubShmObject();
404+
IpcCommsSync* block = StubShmObject();
405+
block->magic_ = 0U;
403406
EXPECT_CALL(*semaphore_mock_, sem_init).WillRepeatedly(Return(SemError(EINVAL)));
407+
EXPECT_CALL(*g_syscall_mock, sem_destroy).Times(0);
404408

405409
EXPECT_EQ(process_launcher->startProcess(pid_, sync_, config_), OsalReturnType::kFail);
410+
EXPECT_EQ(block->magic_, 0U);
406411
}
407412

408413
TEST_F(ProcessLauncherTest, releasingCommsObjectDestroysSemaphore)
@@ -419,6 +424,8 @@ TEST_F(ProcessLauncherTest, releasingCommsObjectDestroysSemaphore)
419424

420425
IpcCommsP block = IpcCommsSync::createCommsObject(fd);
421426
ASSERT_NE(block, nullptr);
427+
EXPECT_EQ(block->magic_, IpcCommsSync::kMagic);
428+
EXPECT_EQ(block->version_, IpcCommsSync::kLayoutVersion);
422429

423430
EXPECT_CALL(*g_syscall_mock, msync(data, _, _)).WillOnce(Return(0));
424431
Expectation destroyed = EXPECT_CALL(*g_syscall_mock, sem_destroy).WillOnce(Return(0));
@@ -797,7 +804,8 @@ TEST_F(StartProcessTest, startProcessChildSuccess)
797804

798805
static_cast<void>(process_launcher->startProcess(pid_, sync_, config_)); // No return from forked process
799806

800-
EXPECT_EQ(block->pid_, forked_pid);
807+
EXPECT_EQ(block->magic_, IpcCommsSync::kMagic);
808+
EXPECT_EQ(block->version_, IpcCommsSync::kLayoutVersion);
801809
}
802810

803811
class ClientMethodsTest : public ProcessLauncherTest

‎score/launch_manager/src/lifecycle_client/src/details/report_running_impl.cpp‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,18 @@ score::Result<std::monostate> ReportRunningImpl::reportKRunningtoDaemon() const
7878
}
7979

8080
// This is our best safeguard against incorrect data treated as an IPCCommsSync
81-
if (sync->pid_ != getpid())
81+
if (sync->magic_ != IpcCommsSync::kMagic)
8282
{
83-
LM_LOG_ERROR() << "[Lifecycle client] Cannot report kRunning from a process not started by Launch Manager";
83+
LM_LOG_ERROR() << "[Lifecycle client] FD " << sync_fd
84+
<< " is not a Launch Manager comms object, cannot report kRunning";
85+
return comms_error;
86+
}
87+
88+
if (sync->version_ != IpcCommsSync::kLayoutVersion)
89+
{
90+
LM_LOG_ERROR() << "[Lifecycle client] Comms object version " << sync->version_
91+
<< " does not match expected version " << IpcCommsSync::kLayoutVersion
92+
<< ", Launch Manager and client are incompatible";
8493
return comms_error;
8594
}
8695

‎score/launch_manager/src/lifecycle_client/src/details/report_running_impl_UT.cpp‎

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,14 @@ using namespace score::mw::lifecycle::internal::osal;
3838
alignas(IpcCommsSync) static char g_fake_ipc_storage[sizeof(IpcCommsSync)];
3939

4040
// Helper to create a fake IPC structure with specified properties
41-
void* CreateFakeIpcComms(pid_t pid)
41+
void* CreateFakeIpcComms(
42+
std::uint32_t magic = IpcCommsSync::kMagic,
43+
std::uint32_t version = IpcCommsSync::kLayoutVersion)
4244
{
4345
std::memset(g_fake_ipc_storage, 0, sizeof(IpcCommsSync));
4446
IpcCommsSync* fake_comms = reinterpret_cast<IpcCommsSync*>(g_fake_ipc_storage);
45-
fake_comms->pid_ = pid;
47+
fake_comms->magic_ = magic;
48+
fake_comms->version_ = version;
4649

4750
return g_fake_ipc_storage;
4851
}
@@ -90,15 +93,12 @@ class ReportRunningImplTest : public ::testing::Test
9093
// By default, close succeeds
9194
EXPECT_CALL(*g_syscall_mock, close(_)).WillRepeatedly(Return(0));
9295

93-
// By default, getpid returns current PID
94-
EXPECT_CALL(*g_syscall_mock, getpid()).WillRepeatedly(Return(::getpid()));
95-
9696
// By default, mmap succeeds and returns valid IPC structure
9797
EXPECT_CALL(*g_syscall_mock, mmap(_, _, _, _, _, _))
9898
.WillRepeatedly(Invoke([](void*, size_t length, int, int, int, off_t) -> void* {
9999
if (length == sizeof(IpcCommsSync))
100100
{
101-
return CreateFakeIpcComms(::getpid());
101+
return CreateFakeIpcComms();
102102
}
103103
return MAP_FAILED;
104104
}));
@@ -296,15 +296,47 @@ TEST_F(ReportRunningImplTest, GivenReportRunningImplInstance_ExceptGetCommsObjec
296296
EXPECT_EQ(result.error(), ExecErrc::kCommunicationError);
297297
}
298298

299-
TEST_F(ReportRunningImplTest, GivenWrongProcessId_WhenPidDoesNotMatch_ThenReturnsCommunicationError)
299+
TEST_F(ReportRunningImplTest, GivenWrongMagic_WhenReportRunningStateCalled_ThenReturnsCommunicationError)
300300
{
301301
RecordProperty("DerivationTechnique", "explorative-testing");
302302
RecordProperty(
303303
"Description",
304-
"Given a ReportRunningImpl instace. "
305-
"Expect a pid mismatch. "
304+
"Given a ReportRunningImpl instance. "
305+
"Expect the shared memory not to contain the Launch Manager magic number. "
306306
"When ReportRunningState() is called. "
307-
"Then ReportRunningState() returns a kCommunicationError error. ");
307+
"Then ReportRunningState() returns a kCommunicationError error and does not post the semaphore. ");
308+
309+
// Given
310+
ReportRunningImpl impl;
311+
312+
// Expect
313+
EXPECT_CALL(*g_syscall_mock, mmap(_, _, _, _, _, _))
314+
.WillOnce([](void*, size_t length, int, int, int, off_t) -> void* {
315+
if (length == sizeof(IpcCommsSync))
316+
{
317+
return CreateFakeIpcComms(0U, IpcCommsSync::kLayoutVersion);
318+
}
319+
return MAP_FAILED;
320+
});
321+
EXPECT_CALL(*semaphore_mock_, sem_post(_)).Times(0);
322+
323+
// When
324+
auto result = impl.ReportRunningState();
325+
326+
// Then
327+
EXPECT_FALSE(result.has_value());
328+
EXPECT_EQ(result.error(), ExecErrc::kCommunicationError);
329+
}
330+
331+
TEST_F(ReportRunningImplTest, GivenWrongLayoutVersion_WhenReportRunningStateCalled_ThenReturnsCommunicationError)
332+
{
333+
RecordProperty("DerivationTechnique", "explorative-testing");
334+
RecordProperty(
335+
"Description",
336+
"Given a ReportRunningImpl instance. "
337+
"Expect the shared memory to contain a different layout version. "
338+
"When ReportRunningState() is called. "
339+
"Then ReportRunningState() returns a kCommunicationError error and does not post the semaphore. ");
308340

309341
// Given
310342
ReportRunningImpl impl;
@@ -314,10 +346,11 @@ TEST_F(ReportRunningImplTest, GivenWrongProcessId_WhenPidDoesNotMatch_ThenReturn
314346
.WillOnce([](void*, size_t length, int, int, int, off_t) -> void* {
315347
if (length == sizeof(IpcCommsSync))
316348
{
317-
return CreateFakeIpcComms(99999);
349+
return CreateFakeIpcComms(IpcCommsSync::kMagic, IpcCommsSync::kLayoutVersion + 1U);
318350
}
319351
return MAP_FAILED;
320352
});
353+
EXPECT_CALL(*semaphore_mock_, sem_post(_)).Times(0);
321354

322355
// When
323356
auto result = impl.ReportRunningState();

0 commit comments

Comments
 (0)