skcms: Increase transform op capacity and add bounds checks Complex color transforms (e.g. A2B to B2A with 4-channel curve tables, matrices, format and space conversion) can require ~40 ops, which exceeded the previous hardcoded limit of 32 ops and resulted in a stack buffer overflow. Increase SKCMS_MAX_PROGRAM_OPS to 64, providing ample padding while keeping stack overhead small. Harden add_op and add_op_ctx to avoid writing past the program buffer during compilation, and add runtime checks in both skcms_Transform and run_program to gracefully reject programs that exceed the maximum capacity. This limit is only hit when doing an A2B to B2A conversion, which Chromium and Android do not do. Fixed: b/530238257 TAG=agy CONV=56f173c3-5496-4fd5-ad6b-34aebcff742d Change-Id: I1868e43405dcd109c01c007ee0ade4d0aebe3198 Reviewed-on: https://skia-review.googlesource.com/c/skcms/+/1351256 Reviewed-by: Florin Malita <fmalita@google.com> Commit-Queue: Christopher Cameron <ccameron@google.com>
diff --git a/skcms.cc b/skcms.cc index 692e6af..c23dbc1 100644 --- a/skcms.cc +++ b/skcms.cc
@@ -2767,20 +2767,28 @@ } // TODO: more careful alias rejection (like, dst == src + 1)? - Op program[32]; - const void* context[32]; + Op program[SKCMS_MAX_PROGRAM_OPS]; + const void* context[SKCMS_MAX_PROGRAM_OPS]; Op* ops = program; const void** contexts = context; auto add_op = [&](Op o) { - *ops++ = o; - *contexts++ = nullptr; + if (ops < program + ARRAY_COUNT(program)) { + *ops = o; + *contexts = nullptr; + } + ops++; + contexts++; }; auto add_op_ctx = [&](Op o, const void* c) { - *ops++ = o; - *contexts++ = c; + if (ops < program + ARRAY_COUNT(program)) { + *ops = o; + *contexts = c; + } + ops++; + contexts++; }; auto add_curve_ops = [&](const skcms_Curve* curves, int numChannels) -> bool { @@ -3099,6 +3107,9 @@ assert(ops <= program + ARRAY_COUNT(program)); assert(contexts <= context + ARRAY_COUNT(context)); + if (ops > program + ARRAY_COUNT(program)) { + return false; + } auto run = baseline::run_program; switch (cpu_type()) {
diff --git a/src/Transform_inl.h b/src/Transform_inl.h index 53a35ef..51f369d 100644 --- a/src/Transform_inl.h +++ b/src/Transform_inl.h
@@ -1565,8 +1565,11 @@ size_t src_bpp, size_t dst_bpp) { #if SKCMS_HAS_MUSTTAIL // Convert the program into an array of tailcall stages. - StageFn stages[32]; + StageFn stages[SKCMS_MAX_PROGRAM_OPS]; assert(programSize <= ARRAY_COUNT(stages)); + if (programSize > ARRAY_COUNT(stages)) { + return; + } static constexpr StageFn kStageFns[] = { #define M(name) &Exec_##name,
diff --git a/src/skcms_Transform.h b/src/skcms_Transform.h index 868d71a..c29b524 100644 --- a/src/skcms_Transform.h +++ b/src/skcms_Transform.h
@@ -118,6 +118,12 @@ /** Constants */ +// The maximum number of ops in a transform program. +// A complex transform (e.g. A2B to B2A with 4 channels, multi-stage curves, matrices, +// format conversion and colorspace conversion) can require up to ~40 ops. +// 64 gives plenty of padding while keeping stack frames small. +static constexpr int SKCMS_MAX_PROGRAM_OPS = 64; + #if defined(__clang__) || defined(__GNUC__) static constexpr float INFINITY_ = __builtin_inff(); #else