Opened 2 years ago

Last modified 12 days ago

#10140 new defect

swscale() crash in Android on x86_64 only (starting from API31)

Reported by: fabienst Owned by:
Priority: important Component: undetermined
Version: git-master Keywords: swscale
Cc: Blocked By:
Blocking: Reproduced by developer: no
Analyzed by developer: no

Description (last modified by fabienst)

I have a crash in my call to sw_scale since Android API 31. I doesn't crash for all inputs, but for now only on the same given picture. It works fine with API 30.
I couldn't reproduce the crash on a device with arm8 + API31. So for now only on x86_64.

I'm calling sw_scale from a native function through JNI which is calling from java.
The input picture is a "Bitmap" object.

The problem still exists in the present snapshot of ffmpeg (2023-01-15). BTW, this LDFLAGS is missing to build for Android: -lnativewindow

If I run the same command from ffmpeg but with directly the jpeg file as input, I don't have a crash, but the input is then a jpeg, while programatically it's a bitmap object.

LD_LIBRARY_PATH=. ./ffmpeg -i /storage/emulated/0/DCIM/test_pics/invalid/image01088.jpg -vf scale=160x45 -sws_flags print_info -sws_flags lanczos -loglevel trace a.jpg

I enabled logging in my program as much as I could. Attached crash_api31.zip​ you have also a sample programme that will reveal the error/crash. It can be run from the command line inside an adb shell.

You will find attached the log for the run on a device running android 11 (=API30) and the log for the run on a device with Android 12 (API31). These were made within the Android emulator.

Crash is reported as:

 A/libc: Fatal signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7e936e915000 in tid 9755 (ServiceStartArg), pid 9722 (adder.app.debug)

Crash is at this line inside swscale() function:

desc[i].process(c, &desc[i], firstCPosY, lastCPosY - firstCPosY + 1);

I attached the input jpeg but also the file that should contain the input of swscale code (if the way I produced them is correct, I'm not so sure of this).

I also attached the material that the android emulator collects to produce bug reports.

For information, the java code: Algo is LANCZOS, but it fails also with others IIRC

    public static Bitmap rescale(Bitmap src, int dstWidth, int dstHeight,
                                 AlgoParametrized1 algo, double p0) {
        int final_algo = algo.flag;
        if (MainApplication.enableLog)
            final_algo |= Algo.SWS_PRINT_INFO.flag;
        return native_rescale(src, Bitmap.createBitmap(dstWidth, dstHeight, src.getConfig()),
                final_algo | Algo.SWS_PRINT_INFO.flag, p0, 0.0);
    }

The C code (but you may look at attached test.c) which goes straight to the reproduction of the issue.

/* SPDX-License-Identifier: (BSD-2-Clause or GPL-2.0-only) */
// Adapted for Exif Thumbnail Adder
// From: https://raw.githubusercontent.com/ser-gik/smoothrescale/master/smoothrescale/src/main/jni/on_load.c

#include <stddef.h>
#include <stdint.h>
#include <jni.h>

#include <android/log.h>
#include <android/bitmap.h>

#include <libswscale/swscale.h>
#include <libavutil/pixfmt.h>
#include <libavutil/log.h>

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "schokoladenbrown", __VA_ARGS__)

struct bitmap {
    jobject           jbitmap;
    AndroidBitmapInfo info;
    uint8_t           *buffer;
};

static int lock_bitmap(JNIEnv *env, struct bitmap *bm) {
    int res = AndroidBitmap_getInfo(env, bm->jbitmap, &bm->info);
    if(ANDROID_BITMAP_RESULT_SUCCESS != res) return res;
    else return AndroidBitmap_lockPixels(env, bm->jbitmap, (void **)&bm->buffer);
}

static int unlock_bitmap(JNIEnv *env, struct bitmap *bm) {
    const static struct bitmap null_bm; 
    int res = AndroidBitmap_unlockPixels(env, bm->jbitmap);
    *bm = null_bm;
    return res;
}

static inline enum AVPixelFormat pix_fmt(enum AndroidBitmapFormat fmt) {
    /* bitmap formats directly correspond to SkColorType values */
    switch (fmt) {
        case ANDROID_BITMAP_FORMAT_RGBA_8888:
            /*
             * kN32_SkColorType
             * Actually it may be one of kBGRA_8888_SkColorType or kRGBA_8888_SkColorType
             * and may be configured at build time. Seems like Android uses RGBA order.
             */
            return AV_PIX_FMT_RGBA;;
        case ANDROID_BITMAP_FORMAT_RGB_565:
            /*
             * kRGB_565_SkColorType
             * This one is packed in native endianness
             */
            return AV_PIX_FMT_RGB565;
        case ANDROID_BITMAP_FORMAT_A_8:
            /*
             * kAlpha_8_SkColorType
             * There is no appropriate AV_PIX_FMT_*
             */
            /* fall through */
        default:
            return AV_PIX_FMT_NONE;
    }
}

static jobject JNICALL native_rescale_impl(JNIEnv *env, jclass clazz,
        jobject srcBitmap, jobject dstBitmap, jint sws_algo, jdouble p0, jdouble p1) {
    struct bitmap src = { .jbitmap = srcBitmap };
    struct bitmap dst = { .jbitmap = dstBitmap };
    jobject ret = NULL;

//    LOGI("algo %x %lf %lf", sws_algo, p0, p1);
    if(ANDROID_BITMAP_RESULT_SUCCESS == lock_bitmap(env, &src)
            && ANDROID_BITMAP_RESULT_SUCCESS == lock_bitmap(env, &dst)) {
        const uint8_t *src_planes[] = { src.buffer };
        const int src_strides[] = { src.info.stride };
        uint8_t *dst_planes[] = { dst.buffer };
        const int dst_strides[] = { dst.info.stride };
        const double params[] = { p0, p1 };
        struct SwsContext *ctx;
        LOGI("%i", __LINE__);
        LOGI("src %i", src.info.format);
        LOGI("srcwidth %i", src.info.width);
        LOGI("srcheight %i", src.info.height);
        LOGI("dst %i", dst.info.format);
        LOGI("dst width %i", dst.info.width);
        LOGI("dst height %i", dst.info.height);
        LOGI("sws_algo %i", sws_algo);

        // Set loglevel (uncomment to debug ffmpeg issues)
        av_log_set_flags(AV_LOG_PRINT_LEVEL | AV_LOG_SKIP_REPEATED);
        av_log_set_level(AV_LOG_TRACE);

        ctx = sws_getContext(src.info.width, src.info.height, pix_fmt(src.info.format),
                             dst.info.width, dst.info.height, pix_fmt(dst.info.format),
                             sws_algo, NULL, NULL, params);
        LOGI("%i", __LINE__);

        if(ctx) {
            LOGI("%i", __LINE__);
            int res = sws_scale(ctx, src_planes, src_strides, 0, src.info.height, dst_planes, dst_strides);
            LOGI("%i", __LINE__);
            sws_freeContext(ctx);
            LOGI("%i", __LINE__);
            if(res > 0) {
                ret = dstBitmap;
            }
        }
    }
    LOGI("%i", __LINE__);
    unlock_bitmap(env, &src);
    LOGI("%i", __LINE__);
    unlock_bitmap(env, &dst);
    return ret;
}


static const char *g_rescaler_java_class = "com/schokoladenbrown/Smooth";
static const JNINativeMethod g_native_methods[] = {
    {"native_rescale",
     "(Landroid/graphics/Bitmap;Landroid/graphics/Bitmap;IDD)Landroid/graphics/Bitmap;",
     native_rescale_impl},
};

jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
    JNIEnv *env = NULL;
    jclass cls;

    (*jvm)->AttachCurrentThread(jvm, &env, NULL);
    cls = (*env)->FindClass(env, g_rescaler_java_class);
    (*env)->RegisterNatives(env, cls, g_native_methods, sizeof g_native_methods / sizeof g_native_methods[0]);
    return JNI_VERSION_1_2;
}

Attachments (9)

log-api30C.txt (41.1 KB ) - added by fabienst 2 years ago.
log-api31C.txt (45.7 KB ) - added by fabienst 2 years ago.
image01088.jpg (85.1 KB ) - added by fabienst 2 years ago.
android_dump.tar.gz-aa (2.3 MB ) - added by fabienst 2 years ago.
android_dump.tar.gz-ab (2.3 MB ) - added by fabienst 2 years ago.
android_dump.tar.gz-ac (183.5 KB ) - added by fabienst 2 years ago.
API31-src (199.2 KB ) - added by fabienst 2 years ago.
src image for swscale() call
API31-dst (28.1 KB ) - added by fabienst 2 years ago.
dst image for swscale() call
crash_api31.zip (1.1 MB ) - added by fabienst 2 years ago.
code sample to trigger the crash

Change History (19)

by fabienst, 2 years ago

Attachment: log-api30C.txt added

by fabienst, 2 years ago

Attachment: log-api31C.txt added

by fabienst, 2 years ago

Attachment: image01088.jpg added

by fabienst, 2 years ago

Attachment: android_dump.tar.gz-aa added

by fabienst, 2 years ago

Attachment: android_dump.tar.gz-ab added

by fabienst, 2 years ago

Attachment: android_dump.tar.gz-ac added

comment:1 by fabienst, 2 years ago

The attached android_dump.tar.gz* files have to concatenated (with cat on linux for example) prior to uncompressing.

Maybe this can be of interest: https://stackoverflow.com/a/63148558/15401262

Version 0, edited 2 years ago by fabienst (next)

comment:2 by fabienst, 2 years ago

Summary: swscale crash in Android starting from API31swscale() crash in Android starting from API31

comment:3 by fabienst, 2 years ago

Priority: normalimportant
Version: unspecifiedgit-master

comment:4 by fabienst, 2 years ago

Additional information:

  • Crash happens on other pictures too.
  • Crash happens on x86_64 (emulated device with api31) but not on arm8 (real device with api 31)
  • Input to swscale() is the same in both cases.
  • To reproduce simply with a call to swscale() I managed to produce the file containg the src & dst arrays. You can find them attached.
    • API30-dst
    • API30-src
    • API31-dst
    • API31-src
  • Other parameter values are:
    • For input: format: AV_PIX_FMT_RGBA, height: 120, stride: 1700, width: 425
    • For output: format: AV_PIX_FMT_RGBA, height: 45, stride: 640, width: 160
    • Algorithm: Lanczos
    • Parameter p0: 3.0
  • Code to produce these files was (for usage in the function native_rescale_impl above):
        FILE* pFile;
        char* yourFilePath  = "/storage/emulated/0/DCIM/test_pics/input";

        // Reserve memory for your readed buffer
        char* readedBuffer = malloc(4);

        if (readedBuffer==0){
            LOGI("Can't reserve memory for Test!");
        }

        // Write your buffer to disk.
        pFile = fopen(yourFilePath,"wb");

        if (pFile){
            fwrite(*src_planes, 4, src.info.width * src.info.height, pFile);
            LOGI("Wrote to file!");
        }
        else{
            LOGI("Something wrong writing to File.");
        }

        fclose(pFile);


        char* outFilePath  = "/storage/emulated/0/DCIM/test_pics/output";
        // Reserve memory for your readed buffer
        int readedBuffer2 = malloc(4);
        if (readedBuffer2==0){
            LOGI("Can't reserve memory for Test!");
        }
        // Write your buffer to disk.
        pFile = fopen(outFilePath,"wb");
        if (pFile){
            fwrite(*dst_planes, 4, dst.info.width * dst.info.height, pFile);
            LOGI("Wrote to file!");
        } else {
            LOGI("Something wrong writing to File.");
        }
        fclose(pFile);

comment:5 by fabienst, 2 years ago

Description: modified (diff)

by fabienst, 2 years ago

Attachment: API31-src added

src image for swscale() call

by fabienst, 2 years ago

Attachment: API31-dst added

dst image for swscale() call

comment:6 by fabienst, 2 years ago

Attached also a sample code that you can build yourself for android: crash_api31.zip​

Building for x86_64

$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android26-clang \
  test.c -I./libs.prebuilt/ffmpeg-4.4/include/ -L$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/26/ -llog -L./libs.prebuilt/ffmpeg-4.4/lib/x86_64 -lswscale -lavutil -o x86_64.out

Running on x86_64 does crash

adb ./libs.prebuilt/ffmpeg-4.4/lib/x86_64/libavutil.so ./libs.prebuilt/ffmpeg-4.4/lib/x86_64/libswscale.so input output x86_64.out /data/local/tmp
adb shell
cd /data/local/tmp
LD_LIBRARY_PATH=. ./x86_64.out

Building for arm64

$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android26-clang \
  test.c -I./libs.prebuilt/ffmpeg-4.4/include/ -L$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/26/ -llog -L./libs.prebuilt/ffmpeg-4.4/lib/arm64-v8a -lswscale -lavutil -o arm64.out

Running on arm64 doesn't crash

adb ./libs.prebuilt/ffmpeg-4.4/lib/arm64-v8a/libavutil.so ./libs.prebuilt/ffmpeg-4.4/lib/arm64-v8a/libswscale.so input output arm64.out /data/local/tmp
adb shell
cd /data/local/tmp
LD_LIBRARY_PATH=. ./arm64.out

Prebuilt libs can be found at https://github.com/tenzap/exif-thumbnail-adder/tree/master/libs.prebuilt

Last edited 2 years ago by fabienst (previous) (diff)

comment:7 by fabienst, 2 years ago

Description: modified (diff)

comment:8 by fabienst, 2 years ago

Summary: swscale() crash in Android starting from API31swscale() crash in Android on x86_64 only (starting from API31)

by fabienst, 2 years ago

Attachment: crash_api31.zip added

code sample to trigger the crash

comment:9 by fabienst, 2 years ago

Description: modified (diff)

comment:10 by Niklas Haas, 12 days ago

Hi,

Can you still reproduce this crash with the current version of FFmpeg? I tried running your code sample but it worked successfully for me, although I was not testing on a real Android device.

Note: See TracTickets for help on using tickets.