Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions man/flac.md
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,10 @@ found by \--keep-foreign-metadata-if-present or \--keep-foreign-metadata
: Instruct the decoder to output an AIFF-C file with format NONE and
sowt respectively.

**\--force-aiff-c-format**={none\|raw\|sowt\|twos\|in24\|42ni\|in32\|23ni}
: Instruct the decoder to output an AIFF-C file with the specified
format.

**\--force-raw-format**
: Force input (when encoding) or output (when decoding) to be treated
as raw samples (even if filename suggests otherwise).
Expand Down
8 changes: 8 additions & 0 deletions oss-fuzz/fuzzer_tool_flac.dict
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@
"--force-wave64-format"
"--force-aiff-c-sowt-format"
"--force-aiff-c-none-format"
"--force-aiff-c-format=none"
"--force-aiff-c-format=raw"
"--force-aiff-c-format=sowt"
"--force-aiff-c-format=twos"
"--force-aiff-c-format=in24"
"--force-aiff-c-format=in32"
"--force-aiff-c-format=42ni"
"--force-aiff-c-format=23ni"
"--force-legacy-wave-format"
"--force-extensible-wave-format"

Expand Down
85 changes: 69 additions & 16 deletions src/flac/decode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1108,14 +1108,40 @@ FLAC__bool write_aiff_form_comm_chunk(FILE *f, FLAC__uint64 samples, uint32_t bp
return false;

if(format == FORMAT_AIFF_C) {
if(subformat == SUBFORMAT_AIFF_C_NONE) {
if(flac__utils_fwrite("NONE", 1, 4, f) != 4)
return false;
const char *compression_format = "NONE";
switch(subformat) {
case SUBFORMAT_AIFF_C_NONE:
compression_format = "NONE";
break;
case SUBFORMAT_AIFF_C_RAW:
compression_format = "raw ";
break;
case SUBFORMAT_AIFF_C_SOWT:
compression_format = "sowt";
break;
case SUBFORMAT_AIFF_C_TWOS:
compression_format = "twos";
break;
case SUBFORMAT_AIFF_C_IN24:
compression_format = "in24";
break;
case SUBFORMAT_AIFF_C_IN32:
compression_format = "in32";
break;
case SUBFORMAT_AIFF_C_42NI:
compression_format = "42ni";
break;
case SUBFORMAT_AIFF_C_23NI:
compression_format = "23ni";
break;
default:
break;
}
else if(subformat == SUBFORMAT_AIFF_C_SOWT) {
if(flac__utils_fwrite("sowt", 1, 4, f) != 4)
return false;

if(flac__utils_fwrite(compression_format, 1, 4, f) != 4) {
return false;
}

for(i = 34; i < comm_length; i++) {
if(flac__utils_fwrite("\x00", 1, 1, f) != 1)
return false;
Expand Down Expand Up @@ -1248,16 +1274,23 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder
FILE *fout = decoder_session->fout;
const uint32_t bps = frame->header.bits_per_sample, channels = frame->header.channels;
const uint32_t shift = (bps%8)? 8-(bps%8): 0;
FLAC__bool is_big_endian = (
(decoder_session->format == FORMAT_AIFF || (decoder_session->format == FORMAT_AIFF_C && decoder_session->subformat == SUBFORMAT_AIFF_C_NONE)) ? true : (
decoder_session->format == FORMAT_WAVE || decoder_session->format == FORMAT_WAVE64 || decoder_session->format == FORMAT_RF64 || (decoder_session->format == FORMAT_AIFF_C && decoder_session->subformat == SUBFORMAT_AIFF_C_SOWT) ? false :
decoder_session->is_big_endian
));
FLAC__bool is_unsigned_samples = (
decoder_session->format == FORMAT_AIFF || decoder_session->format == FORMAT_AIFF_C ? false : (
decoder_session->format == FORMAT_WAVE || decoder_session->format == FORMAT_WAVE64 || decoder_session->format == FORMAT_RF64 ? bps<=8 :
decoder_session->is_unsigned_samples
));
FLAC__bool is_big_endian = ((decoder_session->format == FORMAT_AIFF ||
(decoder_session->format == FORMAT_AIFF_C &&
((decoder_session->subformat == SUBFORMAT_AIFF_C_NONE) ||
(decoder_session->subformat == SUBFORMAT_AIFF_C_RAW) ||
(decoder_session->subformat == SUBFORMAT_AIFF_C_TWOS) ||
(decoder_session->subformat == SUBFORMAT_AIFF_C_IN24) ||
(decoder_session->subformat == SUBFORMAT_AIFF_C_IN32))))
? true
: (
decoder_session->format == FORMAT_WAVE || decoder_session->format == FORMAT_WAVE64 ||
decoder_session->format == FORMAT_RF64 ||
(decoder_session->format == FORMAT_AIFF_C &&
((decoder_session->subformat == SUBFORMAT_AIFF_C_SOWT) ||
(decoder_session->subformat == SUBFORMAT_AIFF_C_42NI) ||
(decoder_session->subformat == SUBFORMAT_AIFF_C_23NI)))
? false
: decoder_session->is_big_endian));
uint32_t wide_samples = frame->header.blocksize, wide_sample, sample, channel;
FLAC__uint64 frame_bytes = 0;

Expand All @@ -1273,6 +1306,26 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder

size_t bytes_to_write = 0;

FLAC__bool is_unsigned_samples = false;
switch(decoder_session->format) {
case FORMAT_WAVE:
case FORMAT_WAVE64:
case FORMAT_RF64:
is_unsigned_samples = (bps <= 8);
break;

case FORMAT_AIFF:
case FORMAT_AIFF_C:
is_unsigned_samples = (bps <= 8) && (decoder_session->subformat == SUBFORMAT_AIFF_C_RAW);
break;

case FORMAT_RAW:
case FORMAT_FLAC:
case FORMAT_OGGFLAC:
is_unsigned_samples = decoder_session->is_unsigned_samples;
break;
}

(void)decoder;

if(decoder_session->abort_flag)
Expand Down
66 changes: 62 additions & 4 deletions src/flac/encode.c
Original file line number Diff line number Diff line change
Expand Up @@ -575,10 +575,15 @@ static FLAC__bool get_sample_info_wave(EncoderSession *e, encode_options_t optio
return true;
}

static inline char get_printable_char(const FLAC__uint8 byte)
{
return ((byte >= 0x20) && (byte <= 0x7E)) ? (char)byte : '?';
}

static FLAC__bool get_sample_info_aiff(EncoderSession *e, encode_options_t options)
{
FLAC__bool got_comm_chunk = false, got_ssnd_chunk = false;
uint32_t sample_rate = 0, channels = 0, bps = 0, shift = 0;
uint32_t sample_rate = 0, channels = 0, bps = 0, original_bps = 0, shift = 0;
FLAC__uint64 sample_frames = 0;
FLAC__uint32 channel_mask = 0;

Expand Down Expand Up @@ -641,6 +646,7 @@ static FLAC__bool get_sample_info_aiff(EncoderSession *e, encode_options_t optio
if(!read_uint16(e->fin, /*big_endian=*/true, &x, e->inbasefilename))
return false;
bps = (uint32_t)x;
original_bps = bps;
shift = (bps%8)? 8-(bps%8) : 0; /* SSND data is always byte-aligned, left-justified but format_input() will double-check */
bps += shift;

Expand All @@ -651,16 +657,68 @@ static FLAC__bool get_sample_info_aiff(EncoderSession *e, encode_options_t optio

/* check compression type for AIFF-C */
if(is_aifc) {
const FLAC__uint32 AIFF_C_COMPRESSION_TYPE_NONE = 0x4E4F4E45; /* "NONE" */
const FLAC__uint32 AIFF_C_COMPRESSION_TYPE_RAW = 0x72617720; /* "raw " */
const FLAC__uint32 AIFF_C_COMPRESSION_TYPE_SOWT = 0x736F7774; /* "sowt" */
const FLAC__uint32 AIFF_C_COMPRESSION_TYPE_TWOS = 0x74776F73; /* "twos" */
const FLAC__uint32 AIFF_C_COMPRESSION_TYPE_IN24 = 0x696E3234; /* "in24" */
const FLAC__uint32 AIFF_C_COMPRESSION_TYPE_IN32 = 0x696E3332; /* "in32" */
const FLAC__uint32 AIFF_C_COMPRESSION_TYPE_42NI = 0x34326E69; /* "42ni" */
const FLAC__uint32 AIFF_C_COMPRESSION_TYPE_23NI = 0x32336E69; /* "23ni" */

if(!read_uint32(e->fin, /*big_endian=*/true, &xx, e->inbasefilename))
return false;
if(xx == 0x736F7774) /* "sowt" */
if((xx == AIFF_C_COMPRESSION_TYPE_SOWT) ||
(xx == AIFF_C_COMPRESSION_TYPE_42NI) ||
(xx == AIFF_C_COMPRESSION_TYPE_23NI)) {
e->info.is_big_endian = false;
else if(xx == 0x4E4F4E45) /* "NONE" */
}
else if((xx == AIFF_C_COMPRESSION_TYPE_NONE) ||
(xx == AIFF_C_COMPRESSION_TYPE_TWOS) ||
(xx == AIFF_C_COMPRESSION_TYPE_IN24) ||
(xx == AIFF_C_COMPRESSION_TYPE_IN32)) {
; /* nothing to do, we already default to big-endian */
}
else if(xx == AIFF_C_COMPRESSION_TYPE_RAW) {
e->info.is_unsigned_samples = true;
}
else {
flac__utils_printf(stderr, 1, "%s: ERROR: can't handle AIFF-C compression type \"%c%c%c%c\"\n", e->inbasefilename, (char)(xx>>24), (char)((xx>>16)&8), (char)((xx>>8)&8), (char)(xx&8));

@madah81pnz1 madah81pnz1 Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This & 8 seems to be a very old bug, all the way from the original implementation in 2005 (commit 15acb69).
It would print either a NUL character or a BS (backspace) character.

I added get_printable_char() to avoid printing garbage characters to the terminal or log file, as this could potentially be exploited in some way wit, e.g. unicode right-to-left override or escape sequence.

flac__utils_printf(stderr, 1, "%s: ERROR: can't handle AIFF-C compression type \"%c%c%c%c\"\n",
e->inbasefilename,
get_printable_char(xx >> 24),
get_printable_char(xx >> 16),
get_printable_char(xx >> 8),
get_printable_char(xx));
return false;
}

{
uint32_t expected_bps = original_bps;

if(xx == AIFF_C_COMPRESSION_TYPE_RAW) {
expected_bps = 8;
}
else if((xx == AIFF_C_COMPRESSION_TYPE_IN24) ||
(xx == AIFF_C_COMPRESSION_TYPE_42NI)) {
expected_bps = 24;
}
else if((xx == AIFF_C_COMPRESSION_TYPE_IN32) ||
(xx == AIFF_C_COMPRESSION_TYPE_23NI)) {
expected_bps = 32;
}

if (original_bps != expected_bps) {
flac__utils_printf(stderr, 1, "%s: ERROR: invalid bits per sample %u, expected %u for AIFF-C compression type \"%c%c%c%c\"\n",
e->inbasefilename,
original_bps,
expected_bps,
get_printable_char(xx >> 24),
get_printable_char(xx >> 16),
get_printable_char(xx >> 8),
get_printable_char(xx));
return false;
}
}
}

/* set channel mapping */
Expand Down
4 changes: 2 additions & 2 deletions src/flac/foreign_metadata.c
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ static FLAC__bool read_from_flac_(foreign_metadata_t *fm, FILE *f, FLAC__Metadat
/* Initialize bools */
fm->is_wavefmtex = 0;
fm->is_aifc = 0;
fm->is_sowt = 0;
memset(fm->aifc_comm_compression_type, 0, sizeof(fm->aifc_comm_compression_type));
fm->is_rf64 = 0 == memcmp(buffer, "RF64", 4);

if(fm->type == FOREIGN_BLOCK_TYPE__RIFF && (0 == memcmp(buffer, "RIFF", 4) || fm->is_rf64))
Expand Down Expand Up @@ -653,7 +653,7 @@ static FLAC__bool read_from_flac_(foreign_metadata_t *fm, FILE *f, FLAC__Metadat
if(error) *error = "read error (020)";
return false;
}
fm->is_sowt = 0 == memcmp(buffer+26, "sowt", 2);
memcpy(fm->aifc_comm_compression_type, buffer + 26, sizeof(fm->aifc_comm_compression_type));
fm->aifc_comm_length = length;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/flac/foreign_metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ typedef struct {
FLAC__bool is_rf64; /* always false if type!=RIFF */
FLAC__bool is_wavefmtex; /* always false if type!=RIFF */
FLAC__bool is_aifc; /* always false if type!=AIFF */
FLAC__bool is_sowt; /* always false if type!=AIFF */
char aifc_comm_compression_type[4]; /* always empty if type!=AIFF */
FLAC__uint32 aifc_comm_length;
FLAC__uint32 ssnd_offset_size; /* 0 if type!=AIFF */
} foreign_metadata_t;
Expand Down
84 changes: 73 additions & 11 deletions src/flac/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ static struct share__option long_options_[] = {
{ "force-extensible-wave-format",share__no_argument,0, 0 },
{ "force-aiff-c-none-format" , share__no_argument, 0, 0 },
{ "force-aiff-c-sowt-format" , share__no_argument, 0, 0 },
{ "force-aiff-c-format" , share__required_argument, 0, 0 },
{ "lax" , share__no_argument, 0, 0 },
{ "replay-gain" , share__no_argument, 0, 0 },
{ "ignore-chunk-sizes" , share__no_argument, 0, 0 },
Expand Down Expand Up @@ -257,6 +258,7 @@ static struct {
FLAC__bool force_extensible_wave_format;
FLAC__bool force_aiff_c_none_format;
FLAC__bool force_aiff_c_sowt_format;
FileSubFormat force_aiff_c_format;
FLAC__bool delete_input;
FLAC__bool preserve_modtime;
FLAC__bool keep_foreign_metadata;
Expand Down Expand Up @@ -443,15 +445,16 @@ int do_it(void)
if(!FLAC__format_sample_rate_is_valid(option_values.format_sample_rate))
return usage_error("ERROR: invalid sample rate '%u', must be > 0 and <= %u\n", option_values.format_sample_rate, FLAC__MAX_SAMPLE_RATE);
}
if((option_values.force_raw_format?1:0) +
(option_values.force_aiff_format?1:0) +
(option_values.force_rf64_format?1:0) +
(option_values.force_wave64_format?1:0) +
(option_values.force_legacy_wave_format?1:0) +
(option_values.force_extensible_wave_format?1:0) +
(option_values.force_aiff_c_none_format?1:0) +
(option_values.force_aiff_c_sowt_format?1:0)
> 1)
if((option_values.force_raw_format ? 1 : 0) +
(option_values.force_aiff_format ? 1 : 0) +
(option_values.force_rf64_format ? 1 : 0) +
(option_values.force_wave64_format ? 1 : 0) +
(option_values.force_legacy_wave_format ? 1 : 0) +
(option_values.force_extensible_wave_format ? 1 : 0) +
(option_values.force_aiff_c_none_format ? 1 : 0) +
(option_values.force_aiff_c_sowt_format ? 1 : 0) +
((option_values.force_aiff_c_format != SUBFORMAT_UNSPECIFIED) ? 1 : 0) >
1)
return usage_error("ERROR: only one of force format options allowed\n");
if(option_values.mode_decode) {
if(!option_values.force_raw_format) {
Expand Down Expand Up @@ -623,6 +626,7 @@ FLAC__bool init_options(void)
option_values.force_extensible_wave_format = false;
option_values.force_aiff_c_none_format = false;
option_values.force_aiff_c_sowt_format = false;
option_values.force_aiff_c_format = SUBFORMAT_UNSPECIFIED;
option_values.delete_input = false;
option_values.preserve_modtime = true;
option_values.keep_foreign_metadata = false;
Expand Down Expand Up @@ -839,6 +843,36 @@ int parse_option(int short_option, const char *long_option, const char *option_a
else if(0 == strcmp(long_option, "force-aiff-c-sowt-format")) {
option_values.force_aiff_c_sowt_format = true;
}
else if(0 == strcmp(long_option, "force-aiff-c-format")) {
FLAC__ASSERT(0 != option_argument);
if(0 == strcmp(option_argument, "none")) {
option_values.force_aiff_c_format = SUBFORMAT_AIFF_C_NONE;
}
else if(0 == strcmp(option_argument, "raw")) {
option_values.force_aiff_c_format = SUBFORMAT_AIFF_C_RAW;
}
else if(0 == strcmp(option_argument, "sowt")) {
option_values.force_aiff_c_format = SUBFORMAT_AIFF_C_SOWT;
}
else if(0 == strcmp(option_argument, "twos")) {
option_values.force_aiff_c_format = SUBFORMAT_AIFF_C_TWOS;
}
else if(0 == strcmp(option_argument, "in24")) {
option_values.force_aiff_c_format = SUBFORMAT_AIFF_C_IN24;
}
else if(0 == strcmp(option_argument, "in32")) {
option_values.force_aiff_c_format = SUBFORMAT_AIFF_C_IN32;
}
else if(0 == strcmp(option_argument, "42ni")) {
option_values.force_aiff_c_format = SUBFORMAT_AIFF_C_42NI;
}
else if(0 == strcmp(option_argument, "23ni")) {
option_values.force_aiff_c_format = SUBFORMAT_AIFF_C_23NI;
}
else {
return usage_error("ERROR: argument to --force-aiff-c-format must be \"none\", \"raw\", \"sowt\", \"twos\", \"in24\", \"42ni\", \"in32\", or \"23ni\"\n");
}
}
else if(0 == strcmp(long_option, "lax")) {
option_values.lax = true;
}
Expand Down Expand Up @@ -1409,6 +1443,7 @@ void show_help(void)
printf(" --force-extensible-wave-format Decode to extensible wave format\n");
printf(" --force-aiff-c-none-format Decode to AIFF-C NONE format\n");
printf(" --force-aiff-c-sowt-format Decode to AIFF-C sowt format\n");
printf(" --force-aiff-c-format=FORMAT Decode to AIFF-C format (none,raw,sowt,twos,in24,42ni,in32,23ni)\n");
printf(" --force-raw-format Treat input or output as raw samples\n");
printf("raw format options:\n");
printf(" --sign={signed|unsigned} Sign of samples (input/output) \n");
Expand Down Expand Up @@ -1922,16 +1957,43 @@ int decode_file(const char *infilename)
output_format = FORMAT_AIFF_C;
output_subformat = SUBFORMAT_AIFF_C_SOWT;
}
else if(option_values.force_aiff_c_format != SUBFORMAT_UNSPECIFIED) {
output_format = FORMAT_AIFF_C;
output_subformat = option_values.force_aiff_c_format;
}
else if(foreign_metadata != NULL) {
if(foreign_metadata->is_wavefmtex)
output_subformat = SUBFORMAT_WAVE_EXTENSIBLE;
else if(output_format == FORMAT_WAVE)
output_subformat = SUBFORMAT_WAVE_PCM;
else if(foreign_metadata->is_aifc) {
if(foreign_metadata->is_sowt)
if(memcmp(foreign_metadata->aifc_comm_compression_type, "NONE", 4) == 0) {
output_subformat = SUBFORMAT_AIFF_C_NONE;
}
else if(memcmp(foreign_metadata->aifc_comm_compression_type, "raw ", 4) == 0) {
output_subformat = SUBFORMAT_AIFF_C_RAW;
}
else if(memcmp(foreign_metadata->aifc_comm_compression_type, "sowt", 4) == 0) {
output_subformat = SUBFORMAT_AIFF_C_SOWT;
else
}
else if(memcmp(foreign_metadata->aifc_comm_compression_type, "twos", 4) == 0) {
output_subformat = SUBFORMAT_AIFF_C_TWOS;
}
else if(memcmp(foreign_metadata->aifc_comm_compression_type, "in24", 4) == 0) {
output_subformat = SUBFORMAT_AIFF_C_IN24;
}
else if(memcmp(foreign_metadata->aifc_comm_compression_type, "in32", 4) == 0) {
output_subformat = SUBFORMAT_AIFF_C_IN32;
}
else if(memcmp(foreign_metadata->aifc_comm_compression_type, "42ni", 4) == 0) {
output_subformat = SUBFORMAT_AIFF_C_42NI;
}
else if(memcmp(foreign_metadata->aifc_comm_compression_type, "23ni", 4) == 0) {
output_subformat = SUBFORMAT_AIFF_C_23NI;
}
else {
output_subformat = SUBFORMAT_AIFF_C_NONE;
}
}
}

Expand Down
Loading