Yes people tampering with your shared memory can compromise most applications.
No, using offset_ptr does not innoculate against that.
As a trivial counter example, imagine a linked list or graph using offset_ptr. If a malicious actor made a cycle in that list, it could lead to DoS by infinite loop if your application doesn't protect against that.
offset_ptr only allows pointer values to be interpreted as the equivalent addresses in separate process spaces, but it provides no validation of the actual value. A simple counter-example here is when you make the pointer point outside the valid range for the shared memory region. It doesn't matter that you store it using offset_ptr, the pointer value will still be invalid.
Access Control
What you seem to be after is access control. This is hard to achieve portably, and as such only limited control is present in Boost Interprocess, in the form of boost::interprocess::permissions:
// In header: <boost/interprocess/permissions.hpp>
class permissions {
public:
// construct/copy/destruct
permissions(os_permissions_type) noexcept;
permissions() noexcept;
// public member functions
void set_default() noexcept;
void set_unrestricted() noexcept;
void set_permissions(os_permissions_type) noexcept;
os_permissions_type get_permissions() const noexcept;
};
The default permissions are a null SECURITY_ATTRIBUTES on windows or 644 filemode on POSIX (u=rw,go=r).
To restrict read and write access to the owner (effectively the user creating the shared memory object) on linux, use e.g. 0600.
Demo
Coliru
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/permissions.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <mutex>
// demo data structure
#include <boost/container/static_vector.hpp>
#include <boost/range/iterator_range.hpp>
#include <fmt/ranges.h>
namespace bip = boost::interprocess;
namespace bc = boost::container;
struct SharedData {
struct Layout {
using Lock = std::unique_lock<bip::interprocess_mutex>;
bip::interprocess_mutex mutable mutex_;
bc::static_vector<int, 1024> vec_{};
Lock obtainLock() const { return Lock(mutex_); }
void add(int i) {
auto lk = obtainLock();
vec_.push_back(i);
}
};
bip::shared_memory_object _smo = [] {
bip::permissions _perms{0600}; // u=rw
bip::shared_memory_object smo(bip::open_or_create, "demo_shared",
bip::mode_t::read_write, _perms);
smo.truncate(sizeof(Layout));
return smo;
}();
bip::mapped_region _mr{_smo, bip::mode_t::read_write, 0, sizeof(Layout)};
void add(int i) { raw().add(i); }
auto list() {
auto lk = raw().obtainLock();
return std::make_pair(std::move(lk),
boost::make_iterator_range(raw().vec_));
}
private:
Layout& raw() { return *reinterpret_cast<Layout*>(_mr.get_address()); }
};
int main(int argc, char** argv) {
auto const addable = std::vector(argv + 1, argv + argc);
SharedData data;
auto dump = [&data] {
auto [lk, ls] = data.list();
fmt::print("Existing: {}\n", ls);
};
dump();
for (auto i : addable)
{
fmt::print("Adding element: {}\n", i);
data.add(std::stoi(i));
dump();
}
}
When run with e.g. sotest 1 2 3 prints e.g.
$ ./build/sotest 1
Existing: []
Adding element: 1
Existing: [1]
$ ./build/sotest 2 3 4
Existing: [1]
Adding element: 2
Existing: [1, 2]
Adding element: 3
Existing: [1, 2, 3]
Adding element: 4
Existing: [1, 2, 3, 4]
And the shared memory mode is -rw-------:
stat /dev/shm/demo_shared
File: /dev/shm/demo_shared
Size: 4144 Blocks: 16 IO Block: 4096 regular file
Device: 18h/24d Inode: 29 Links: 1
Access: (0600/-rw-------) Uid: ( 2000/ sehe) Gid: ( 2000/ sehe)
Access: 2022-06-28 00:25:47.167492907 +0200
Modify: 2022-06-28 00:25:10.559796466 +0200
Change: 2022-06-28 00:25:10.559796466 +0200
Birth: -
