Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 86 additions & 12 deletions .github/actions/environment-check/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,66 @@ runs:
run: |
echo "===== Deploying cached deps to system ====="
DEPS_DIR="$RUNNER_TEMP/dvledtx-deps"
if [ -d "$DEPS_DIR" ]; then
# DESTDIR-captured tree has structure DEPS_DIR/usr/local/...; copy to /
sudo cp -rp "$DEPS_DIR/." /
if [ ! -d "$DEPS_DIR" ]; then
echo " No cached deps found, will build from source."
exit 0
fi

# Bulk copy the full DESTDIR tree to /
sudo cp -rp "$DEPS_DIR/." /
sudo ldconfig
echo " Cache deployed. Size: $(du -sh "$DEPS_DIR" | cut -f1)"

# --- Explicit FFmpeg binary copy and restore confirmation ---
echo ""
echo " --- FFmpeg explicit restore check ---"
FFMPEG_SRC_BIN=$(find "$DEPS_DIR" -type f -name "ffmpeg" | head -1)
FFMPEG_LIB_DIR=$(find "$DEPS_DIR" -type d -name "lib" | grep -v pkgconfig | head -1)

# Copy FFmpeg binary explicitly if found in DESTDIR snapshot
if [ -n "$FFMPEG_SRC_BIN" ]; then
DEST_BIN="/usr/local/bin/ffmpeg"
sudo cp -p "$FFMPEG_SRC_BIN" "$DEST_BIN"
echo " [OK] Copied ffmpeg binary: $FFMPEG_SRC_BIN -> $DEST_BIN"
else
echo " [WARN] ffmpeg binary not found in cache snapshot ($DEPS_DIR)"
fi

# Copy FFmpeg shared libraries explicitly
if [ -n "$FFMPEG_LIB_DIR" ]; then
sudo find "$FFMPEG_LIB_DIR" -name "libav*.so*" -o -name "libsw*.so*" | \
while read -r SOFILE; do
sudo cp -p "$SOFILE" /usr/local/lib/
echo " [OK] Copied $(basename "$SOFILE")"
done
sudo ldconfig
echo " Cache deployed. Size: $(du -sh "$DEPS_DIR" | cut -f1)"
else
echo " No cached deps found, will build from source."
echo " [WARN] FFmpeg lib dir not found in cache snapshot"
fi

# Confirm each FFmpeg lib is registered
echo ""
echo " --- FFmpeg restore confirmation ---"
ALL_OK=1
for LIB in libavformat libavcodec libavutil libavdevice libswscale; do
LIB_PATH=$(ldconfig -p | grep "^\s*${LIB}\.so" | awk '{print $NF}' | head -1)
if [ -n "$LIB_PATH" ]; then
echo " [OK] $LIB -> $LIB_PATH"
else
echo " [FAIL] $LIB not found after restore"
ALL_OK=0
fi
done
FFMPEG_BIN=$(command -v ffmpeg 2>/dev/null || true)
if [ -n "$FFMPEG_BIN" ]; then
echo " [OK] ffmpeg binary in PATH: $FFMPEG_BIN"
else
echo " [FAIL] ffmpeg binary not found in PATH after restore"
ALL_OK=0
fi
if [ "$ALL_OK" -eq 0 ]; then
echo "ERROR: FFmpeg restore incomplete — cache may be corrupt."
exit 1
fi

- name: Install APT packages
Expand Down Expand Up @@ -185,10 +238,8 @@ runs:
run: |
echo "===== FFmpeg Setup ====="
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/usr/local/lib/x86_64-linux-gnu/pkgconfig:${PKG_CONFIG_PATH:-}
if pkg-config --exists libavformat libavcodec libavutil libavdevice 2>/dev/null \
&& [ -x "/usr/local/bin/ffmpeg" ] \
&& /usr/local/bin/ffmpeg -version 2>&1 | grep -q -- '--enable-mtl'; then
echo " [OK] FFmpeg with MTL plugin already installed — skipping build (cached)"
if pkg-config --exists libavformat libavcodec libavutil libavdevice 2>/dev/null; then
echo " [OK] FFmpeg already installed — skipping build (cached)"
exit 0
fi
echo " Building FFmpeg $FFMPEG_VERSION with MTL plugin from source..."
Expand All @@ -200,13 +251,13 @@ runs:
git apply "$MTL_SOURCE"/ecosystem/ffmpeg_plugin/"$FFMPEG_VERSION"/*.patch
cp "$MTL_SOURCE"/ecosystem/ffmpeg_plugin/mtl_*.c libavdevice/
cp "$MTL_SOURCE"/ecosystem/ffmpeg_plugin/mtl_*.h libavdevice/
./configure --enable-shared --disable-static --enable-pic \
./configure --enable-shared --disable-static --enable-pic \
--enable-libopenh264 --enable-encoder=libopenh264 --enable-mtl
make -j "$(nproc)"
make install DESTDIR="$RUNNER_TEMP/dvledtx-deps"
sudo make install
sudo ldconfig
echo " FFmpeg installed: $(ffmpeg -version 2>&1 | head -1)"
echo " FFmpeg installed."

- name: Verify build dependencies
shell: bash
Expand All @@ -233,8 +284,31 @@ runs:
shell: bash
run: |
echo "===== FFmpeg MTL Plugin Verification ====="
echo " ffmpeg: $(ffmpeg -version 2>&1 | head -1)"

# Locate the binary first
FFMPEG_PATH=$(which ffmpeg 2>/dev/null || true)
if [ -z "$FFMPEG_PATH" ]; then
echo "ERROR: ffmpeg not found in PATH (which ffmpeg returned nothing)."
echo " PATH=$PATH"
exit 1
fi
echo " [OK] ffmpeg binary: $FFMPEG_PATH"

# Run ffmpeg -version and print full output
echo " --- ffmpeg -version output ---"
FFMPEG_VERSION_OUT=$(ffmpeg -version 2>&1)
echo "$FFMPEG_VERSION_OUT"
FFMPEG_VER=$(echo "$FFMPEG_VERSION_OUT" | head -1)
if [ -z "$FFMPEG_VER" ]; then
echo "ERROR: ffmpeg -version returned no output."
exit 1
fi
echo " [OK] ffmpeg version: $FFMPEG_VER"

# Check MTL device is registered
echo " --- ffmpeg -devices output ---"
DEVICES=$(ffmpeg -devices 2>&1)
echo "$DEVICES"
if echo "$DEVICES" | grep -qi "mtl"; then
MTL_DEVICES=$(echo "$DEVICES" | grep -i "mtl")
echo " [OK] MTL device(s) registered in FFmpeg avdevices:"
Expand Down
1 change: 0 additions & 1 deletion .github/actions/smoke-tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,3 @@ runs:
echo "Smoke test passed for $NAME. Output:"
cat "$OUTFILE"
done

8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,14 @@ MTL uses VFIO to access the NIC. The current user must belong to the `vfio` grou
id -nG $USER
```

### Killing the Application

If `dvledtx` becomes unresponsive or needs to be force-stopped:

```bash
sudo pkill -9 -f dvledtx
```

### Common Issues

1. **MTL Initialization Failed**
Expand Down
6 changes: 6 additions & 0 deletions Security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Security Policy
Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation.

## Reporting a Vulnerability
Please report any security vulnerabilities in this project [utilizing the guidelines here](https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html).

14 changes: 14 additions & 0 deletions include/util/logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,20 @@ void logger_log(log_level_t level, const char *file, int line,
*/
bool logger_is_level_enabled(log_level_t level);

/**
* @brief Override the log rotation size threshold (default 20 MB).
* Intended for unit testing only.
*
* @param size_bytes New rotation threshold in bytes
*/
void logger_set_max_size(long size_bytes);

/**
* @brief Tell the logger that stdout/stderr have been dup2'd to the log file.
* After log rotation, the logger will re-dup2 them to the new file.
*/
void logger_set_stdout_redirected(bool redirected);

/* Convenience macros for logging */
#define LOG_ERROR(fmt, ...) \
logger_log(LOG_LEVEL_ERROR, __FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__)
Expand Down
59 changes: 12 additions & 47 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <linux/capability.h>
#include <sys/syscall.h>
#include <limits.h>

/* libavdevice is only needed for the FFmpeg mtl_st20p muxer TX path */
Expand Down Expand Up @@ -52,44 +48,6 @@ static void dvledtx_apply_pending_signal_exit(void) {
if (g_app_ptr != NULL) g_app_ptr->exit = true;
}

/* =========================================================================
* E-1: Privilege drop — reduce capabilities after DPDK/MTL initialisation.
*
* dvledtx requires CAP_SYS_ADMIN (VFIO) and CAP_IPC_LOCK (hugepages) during
* mtl_init / session_manager_init. Once the NIC is bound and hugepages are
* locked, drop to the minimal set so that any subsequent exploit (e.g. via
* libavcodec) does not grant kernel-level access.
* ========================================================================= */
static void drop_privileges(void) {
/* Keep only CAP_IPC_LOCK (for hugepages) and CAP_NET_ADMIN (NIC control).
* Use the raw syscall interface to avoid linking libcap. */
struct __user_cap_header_struct hdr = {
.version = _LINUX_CAPABILITY_VERSION_3,
.pid = 0 /* current process */
};
struct __user_cap_data_struct data[2];
memset(data, 0, sizeof(data));

/* CAP_IPC_LOCK = 14, CAP_NET_ADMIN = 12 */
uint32_t caps = (1U << 14) | (1U << 12);
data[0].effective = caps;
data[0].permitted = caps;
data[0].inheritable = 0;
data[1].effective = 0;
data[1].permitted = 0;
data[1].inheritable = 0;

/* Prevent regaining caps via execve */
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);

if (syscall(SYS_capset, &hdr, data) < 0) {
LOG_WARN("drop_privileges: capset failed (errno=%d) — "
"running with elevated privileges", errno);
} else {
LOG_INFO("Privileges dropped to CAP_NET_ADMIN+CAP_IPC_LOCK");
}
}

/* =========================================================================
* E-5: Log file path validation — restrict to safe directories.
*
Expand All @@ -98,7 +56,6 @@ static void drop_privileges(void) {
* ========================================================================= */
static const char* ALLOWED_LOG_PREFIXES[] = {
"/var/log/",
"/tmp/",
NULL /* sentinel — also allows paths relative to cwd (resolved below) */
};

Expand Down Expand Up @@ -228,6 +185,17 @@ int main(int argc, char** argv) {
goto cleanup_logger;
}

/* Reject symlinked config files to prevent symlink-based attacks */
{
struct stat lst;
if (lstat(app.config_file, &lst) == 0 && S_ISLNK(lst.st_mode)) {
LOG_ERROR("Config file '%s' is a symbolic link — rejected for security",
app.config_file);
ret = -1;
goto cleanup_logger;
}
}

/* Phase 2: resolve log file destination BEFORE the full config load so that
* "Config loaded" and session-info messages go directly to the log file.
* Priority: config "log_file" > LOG_FILE env variable > console only. */
Expand Down Expand Up @@ -266,6 +234,7 @@ int main(int argc, char** argv) {
LOG_WARN("setvbuf(stdout) failed");
if (setvbuf(stderr, NULL, _IOLBF, 0) != 0)
LOG_WARN("setvbuf(stderr) failed");
logger_set_stdout_redirected(true);
redirected = true;
} else {
LOG_WARN("dup2 failed for log redirection");
Expand Down Expand Up @@ -333,10 +302,6 @@ int main(int argc, char** argv) {
goto cleanup_logger;
}

/* E-1: Drop elevated privileges — DPDK/MTL initialization is complete,
* hugepages are locked, VFIO group is open. No longer need CAP_SYS_ADMIN. */
drop_privileges();

/* Start transmission sessions */
if (session_manager_start(&session_manager) < 0) {
LOG_ERROR("Failed to start sessions");
Expand Down
4 changes: 2 additions & 2 deletions src/util/config_reader.c
Original file line number Diff line number Diff line change
Expand Up @@ -417,8 +417,8 @@ int validate_tx_config(const struct dvledtx_config* config) {
LOG_ERROR("video width/height must be non-zero");
return -1;
}
if (config->width > 7680 || config->height > 4320) {
LOG_ERROR("video resolution %dx%d exceeds maximum 7680x4320",
if (config->width > 1920 || config->height > 1080) {
LOG_ERROR("video resolution %dx%d exceeds maximum 1920x1080",
config->width, config->height);
return -1;
}
Expand Down
Loading
Loading