-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathmain.cpp
More file actions
88 lines (75 loc) · 3.38 KB
/
Copy pathmain.cpp
File metadata and controls
88 lines (75 loc) · 3.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// SPDX-License-Identifier: AGPL-3.0-or-later
#include "hook.hpp"
#include "webhook_handler.hpp"
#include <stdlib.h>
// ---------------------------------------------------------------------------
// The old constructor called hook() synchronously, but under musl the
// LD_PRELOAD library's constructor runs *before* the main binary is loaded
// → dl_iterate_phdr returned 0 callbacks, and /proc/self/maps doesn't yet
// contain the PMS text segment → hook() = no-op.
//
// We now combine two strategies:
//
// 1. Try hook() immediately (best-effort – will fail if the binary
// isn't mapped yet, which is both harmless and informative).
//
// 2. Override a function that PMS calls during runtime startup but well
// after all libraries are loaded. When that override first fires, it
// calls hook() and then passes through to the real function.
//
// Strategy 2 uses a trigger that's guaranteed to fire during normal PMS
// operation but AFTER boost::uuids, epoll, and the HTTP server are all
// initialised. We override `epoll_create1` (called by the event loop)
// rather than `bind` (which the musl dynamic linker may reference during
// loading, causing boost UUID crashes).
//
// Both strategies are idempotent: hook() is guarded by the TextSpan check
// on the inside (returns immediately if text range not found / hooks
// already set), and the deferred trigger is a one-shot flag.
// ---------------------------------------------------------------------------
static int g_hooks_attempted = 0;
// ---- deferred trigger: epoll_create1 override ----
//
// PMS uses epoll for its event loop. The first epoll_create1 call happens
// during server initialisation, long after the main binary is fully mapped.
// This is a safer trigger than bind (which libc's NSS/resolver may call
// during dlopen, triggering boost::uuids crashes).
#include <sys/epoll.h>
#include <dlfcn.h>
#include <unistd.h>
#include <sys/syscall.h>
typedef int (*RealEpollFn)(int);
static int g_epoll_triggered = 0;
int epoll_create1(int flags)
{
if(!g_epoll_triggered)
{
g_epoll_triggered = 1;
hook();
RealEpollFn real = (RealEpollFn)dlsym(RTLD_NEXT, "epoll_create1");
if(real)
return real(flags);
return syscall(SYS_epoll_create1, flags);
}
RealEpollFn real = (RealEpollFn)dlsym(RTLD_NEXT, "epoll_create1");
if(real)
return real(flags);
return syscall(SYS_epoll_create1, flags);
}
__attribute__((constructor)) void init_so()
{
// This library is LD_PRELOADed into the (musl) "Plex Media Server" process.
// Plex later spawns glibc /bin/sh helpers (Plex Tuner Service, Plex Script
// Host, transcoders) which would inherit LD_PRELOAD and fail to load this
// musl .so ("/bin/sh: error while loading shared libraries"). The constructor
// runs before main() and before any child is spawned, so clearing LD_PRELOAD
// here scopes the preload to this process only.
unsetenv("LD_PRELOAD");
// Socket hooks in webhook_handler.cpp are active as soon as the library is
// preloaded. Resolve their real libc targets before PMS startup code reads
// from /dev/urandom; otherwise our read() interposer would fail early reads.
webhook_handler_init();
// Strategy 1: skipped — hook() will be triggered by the first
// epoll_create1 call, which happens during PMS's event loop init
// after everything is loaded.
}