diff --git a/pngpriv.h b/pngpriv.h index 9ddc65569..4adb58818 100644 --- a/pngpriv.h +++ b/pngpriv.h @@ -901,13 +901,6 @@ PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr, png_const_uint_16p hist, int num_hist)); #endif -/* Chunks that have keywords */ -#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_pCAL_SUPPORTED) || \ - defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) -PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr, - png_const_charp key, png_charpp new_key)); -#endif - #ifdef PNG_WRITE_tEXt_SUPPORTED PNG_EXTERN void png_write_tEXt PNGARG((png_structp png_ptr, png_const_charp key, png_const_charp text, png_size_t text_len)); diff --git a/pngset.c b/pngset.c index 844c317c3..94c1b13fa 100644 --- a/pngset.c +++ b/pngset.c @@ -20,6 +20,60 @@ #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_pCAL_SUPPORTED) ||\ + defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) +/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification, + * and if invalid, correct the keyword rather than discarding the entire + * chunk. The PNG 1.0 specification requires keywords 1-79 characters in + * length, forbids leading or trailing whitespace, multiple internal spaces, + * and the non-break space (0x80) from ISO 8859-1. Returns keyword length. + * + * The 'new_key' buffer must be 80 characters in size (for the keyword plus a + * trailing '\0'). If this routine returns 0 then there was no keyword, or a + * valid one could not be generated, and the caller must handle the error by not + * setting the keyword. + */ +static png_uint_32 +png_check_keyword(png_const_charp key, png_bytep new_key) +{ + png_uint_32 key_len = 0; + int space = 1; + + if (key == NULL) + { + *new_key = 0; + return 0; + } + + while (*key && key_len < 79) + { + png_byte ch = (png_byte)*key++; + + if ((ch > 32 && ch <= 126) || (ch >= 161 /*&& ch <= 255*/)) + *new_key++ = ch, ++key_len, space = 0; + + else if (space == 0) + { + /* A space or an invalid character when one wasn't seen immediately + * before; output just a space. + */ + *new_key++ = 32, ++key_len, space = 1; + } + } + + if (key_len > 0 && space != 0) /* trailing space */ + --key_len, --new_key; + + /* Terminate the keyword */ + *new_key = 0; + + if (key_len == 0) + return 0; + + return key_len; +} +#endif /* TEXT || pCAL || iCCP || sPLT */ + #ifdef PNG_bKGD_SUPPORTED void PNGAPI png_set_bKGD(png_structp png_ptr, png_infop info_ptr, @@ -278,6 +332,7 @@ png_set_pCAL(png_structp png_ptr, png_infop info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams, png_const_charp units, png_charpp params) { + png_byte new_purpose[80]; png_size_t length; int i; @@ -286,7 +341,15 @@ png_set_pCAL(png_structp png_ptr, png_infop info_ptr, if (png_ptr == NULL || info_ptr == NULL) return; - length = png_strlen(purpose) + 1; + length = png_check_keyword(purpose, new_purpose); + + if (length == 0) + { + png_warning(png_ptr, "pCAL: invalid purpose keyword"); + return; + } + + ++length; png_debug1(3, "allocating purpose for info (%lu bytes)", (unsigned long)length); @@ -309,7 +372,7 @@ png_set_pCAL(png_structp png_ptr, png_infop info_ptr, return; } - png_memcpy(info_ptr->pcal_purpose, purpose, length); + png_memcpy(info_ptr->pcal_purpose, new_purpose, length); png_debug(3, "storing X0, X1, type, and nparams in info"); info_ptr->pcal_X0 = X0; @@ -614,6 +677,7 @@ png_set_iCCP(png_structp png_ptr, png_infop info_ptr, png_const_charp name, int compression_type, png_const_bytep profile, png_uint_32 proflen) { + png_byte new_name[80]; png_charp new_iccp_name; png_bytep new_iccp_profile; png_size_t length; @@ -623,7 +687,15 @@ png_set_iCCP(png_structp png_ptr, png_infop info_ptr, if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL) return; - length = png_strlen(name)+1; + length = png_check_keyword(name, new_name); + + if (length == 0) + { + png_warning(png_ptr, "iCCP: invalid keyword"); + return; + } + + ++length; new_iccp_name = (png_charp)png_malloc_warn(png_ptr, length); if (new_iccp_name == NULL) @@ -632,7 +704,7 @@ png_set_iCCP(png_structp png_ptr, png_infop info_ptr, return; } - png_memcpy(new_iccp_name, name, length); + png_memcpy(new_iccp_name, new_name, length); new_iccp_profile = (png_bytep)png_malloc_warn(png_ptr, proflen); if (new_iccp_profile == NULL) @@ -748,6 +820,7 @@ png_set_text_2(png_structp png_ptr, png_infop info_ptr, } for (i = 0; i < num_text; i++) { + png_byte new_key[80], new_lang[80]; png_size_t text_length, key_len; png_size_t lang_len, lang_key_len; png_textp textp = &(info_ptr->text[info_ptr->num_text]); @@ -762,7 +835,13 @@ png_set_text_2(png_structp png_ptr, png_infop info_ptr, continue; } - key_len = png_strlen(text_ptr[i].key); + key_len = png_check_keyword(text_ptr[i].key, new_key); + + if (key_len == 0) + { + png_warning(png_ptr, "invalid text keyword"); + continue; + } if (text_ptr[i].compression <= 0) { @@ -775,8 +854,9 @@ png_set_text_2(png_structp png_ptr, png_infop info_ptr, { /* Set iTXt data */ + /* Zero length language is OK */ if (text_ptr[i].lang != NULL) - lang_len = png_strlen(text_ptr[i].lang); + lang_len = png_check_keyword(text_ptr[i].lang, new_lang); else lang_len = 0; @@ -824,7 +904,7 @@ png_set_text_2(png_structp png_ptr, png_infop info_ptr, (key_len + lang_len + lang_key_len + text_length + 4), textp->key); - png_memcpy(textp->key, text_ptr[i].key,(png_size_t)(key_len)); + png_memcpy(textp->key, new_key, (png_size_t)(key_len)); *(textp->key + key_len) = '\0'; if (text_ptr[i].compression > 0) @@ -976,7 +1056,7 @@ png_set_sPLT(png_structp png_ptr, */ { png_sPLT_tp np; - int i; + int i, j; size_t element_size; if (png_ptr == NULL || info_ptr == NULL) @@ -1008,13 +1088,22 @@ png_set_sPLT(png_structp png_ptr, png_free(png_ptr, info_ptr->splt_palettes); info_ptr->splt_palettes=NULL; - for (i = 0; i < nentries; i++) + for (i = j = 0; i < nentries; i++) { - png_sPLT_tp to = np + info_ptr->splt_palettes_num + i; + png_sPLT_tp to = np + info_ptr->splt_palettes_num + j; png_const_sPLT_tp from = entries + i; + png_byte new_name[80]; png_size_t length; - length = png_strlen(from->name) + 1; + length = png_check_keyword(from->name, new_name); + + if (length == 0) + { + png_warning(png_ptr, "sPLT: invalid keyword"); + continue; + } + + ++length; /* for trailing '\0' */ to->name = (png_charp)png_malloc_warn(png_ptr, length); if (to->name == NULL) @@ -1024,7 +1113,7 @@ png_set_sPLT(png_structp png_ptr, continue; } - png_memcpy(to->name, from->name, length); + png_memcpy(to->name, new_name, length); to->entries = (png_sPLT_entryp)png_malloc_warn(png_ptr, from->nentries * png_sizeof(png_sPLT_entry)); @@ -1042,10 +1131,11 @@ png_set_sPLT(png_structp png_ptr, to->nentries = from->nentries; to->depth = from->depth; + ++j; } info_ptr->splt_palettes = np; - info_ptr->splt_palettes_num += nentries; + info_ptr->splt_palettes_num = j; info_ptr->valid |= PNG_INFO_sPLT; info_ptr->free_me |= PNG_FREE_SPLT; } @@ -1348,137 +1438,4 @@ png_set_check_for_invalid_index(png_structp png_ptr, int allowed) png_ptr->num_palette_max = -1; } #endif - -#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_pCAL_SUPPORTED) || \ - defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) -/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification, - * and if invalid, correct the keyword rather than discarding the entire - * chunk. The PNG 1.0 specification requires keywords 1-79 characters in - * length, forbids leading or trailing whitespace, multiple internal spaces, - * and the non-break space (0x80) from ISO 8859-1. Returns keyword length. - * - * The new_key is allocated to hold the corrected keyword and must be freed - * by the calling routine. This avoids problems with trying to write to - * static keywords without having to have duplicate copies of the strings. - */ -png_size_t /* PRIVATE */ -png_check_keyword(png_structp png_ptr, png_const_charp key, png_charpp new_key) -{ - png_size_t key_len; - png_const_charp ikp; - png_charp kp, dp; - int kflag; - int kwarn=0; - - png_debug(1, "in png_check_keyword"); - - *new_key = NULL; - - if (key == NULL || (key_len = png_strlen(key)) == 0) - { - png_warning(png_ptr, "zero length keyword"); - return ((png_size_t)0); - } - - png_debug1(2, "Keyword to be checked is '%s'", key); - - *new_key = (png_charp)png_malloc_warn(png_ptr, (png_uint_32)(key_len + 2)); - - if (*new_key == NULL) - { - png_warning(png_ptr, "Out of memory while procesing keyword"); - return ((png_size_t)0); - } - - /* Replace non-printing characters with a blank and print a warning */ - for (ikp = key, dp = *new_key; *ikp != '\0'; ikp++, dp++) - { - if ((png_byte)*ikp < 0x20 || - ((png_byte)*ikp > 0x7E && (png_byte)*ikp < 0xA1)) - { - PNG_WARNING_PARAMETERS(p) - - png_warning_parameter_unsigned(p, 1, PNG_NUMBER_FORMAT_02x, - (png_byte)*ikp); - png_formatted_warning(png_ptr, p, "invalid keyword character 0x@1"); - *dp = ' '; - } - - else - { - *dp = *ikp; - } - } - *dp = '\0'; - - /* Remove any trailing white space. */ - kp = *new_key + key_len - 1; - if (*kp == ' ') - { - png_warning(png_ptr, "trailing spaces removed from keyword"); - - while (key_len && *kp == ' ') - { - *(kp--) = '\0'; - key_len--; - } - } - - /* Remove any leading white space. */ - kp = *new_key; - if (*kp == ' ') - { - png_warning(png_ptr, "leading spaces removed from keyword"); - - while (*kp == ' ') - { - kp++; - key_len--; - } - } - - png_debug1(2, "Checking for multiple internal spaces in '%s'", kp); - - /* Remove multiple internal spaces. */ - for (kflag = 0, dp = *new_key; *kp != '\0'; kp++) - { - if (*kp == ' ' && kflag == 0) - { - *(dp++) = *kp; - kflag = 1; - } - - else if (*kp == ' ') - { - key_len--; - kwarn = 1; - } - - else - { - *(dp++) = *kp; - kflag = 0; - } - } - *dp = '\0'; - if (kwarn != 0) - png_warning(png_ptr, "extra interior spaces removed from keyword"); - - if (key_len == 0) - { - png_free(png_ptr, *new_key); - png_warning(png_ptr, "Zero length keyword"); - } - - if (key_len > 79) - { - png_warning(png_ptr, "keyword length must be 1 - 79 characters"); - (*new_key)[79] = '\0'; - key_len = 79; - } - - return (key_len); -} -#endif /* TEXT || pCAL) || iCCP || sPLT */ - #endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/pngwutil.c b/pngwutil.c index b7da0e554..e4f5452b8 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -1099,7 +1099,6 @@ png_write_iCCP(png_structp png_ptr, png_const_charp name, int compression_type, png_const_charp profile, int profile_len) { png_size_t name_len; - png_charp new_name; compression_state comp; int embedded_profile_len = 0; @@ -1111,8 +1110,7 @@ png_write_iCCP(png_structp png_ptr, png_const_charp name, int compression_type, comp.input = NULL; comp.input_len = 0; - if ((name_len = png_check_keyword(png_ptr, name, &new_name)) == 0) - return; + name_len = png_strlen(name); if (compression_type != PNG_COMPRESSION_TYPE_BASE) png_warning(png_ptr, "Unknown compression type in iCCP chunk"); @@ -1131,8 +1129,6 @@ png_write_iCCP(png_structp png_ptr, png_const_charp name, int compression_type, { png_warning(png_ptr, "Embedded profile length in iCCP chunk is negative"); - - png_free(png_ptr, new_name); return; } @@ -1140,8 +1136,6 @@ png_write_iCCP(png_structp png_ptr, png_const_charp name, int compression_type, { png_warning(png_ptr, "Embedded profile length too large in iCCP chunk"); - - png_free(png_ptr, new_name); return; } @@ -1161,10 +1155,14 @@ png_write_iCCP(png_structp png_ptr, png_const_charp name, int compression_type, png_write_chunk_header(png_ptr, png_iCCP, (png_uint_32)(name_len + profile_len + 2)); - new_name[name_len + 1] = 0x00; + png_write_chunk_data(png_ptr, (png_bytep)name, name_len); - png_write_chunk_data(png_ptr, (png_bytep)new_name, - (png_size_t)(name_len + 2)); + { + png_byte buffer[2]; + buffer[0] = 0; /* terminate name */ + buffer[1] = 0xFFU & compression_type; + png_write_chunk_data(png_ptr, buffer, 2); + } if (profile_len != 0) { @@ -1172,7 +1170,6 @@ png_write_iCCP(png_structp png_ptr, png_const_charp name, int compression_type, } png_write_chunk_end(png_ptr); - png_free(png_ptr, new_name); } #endif @@ -1182,7 +1179,6 @@ void /* PRIVATE */ png_write_sPLT(png_structp png_ptr, png_const_sPLT_tp spalette) { png_size_t name_len; - png_charp new_name; png_byte entrybuf[10]; png_size_t entry_size = (spalette->depth == 8 ? 6 : 10); png_size_t palette_size = entry_size * spalette->nentries; @@ -1193,14 +1189,13 @@ png_write_sPLT(png_structp png_ptr, png_const_sPLT_tp spalette) png_debug(1, "in png_write_sPLT"); - if ((name_len = png_check_keyword(png_ptr,spalette->name, &new_name))==0) - return; + name_len = png_strlen(spalette->name); /* Make sure we include the NULL after the name */ png_write_chunk_header(png_ptr, png_sPLT, (png_uint_32)(name_len + 2 + palette_size)); - png_write_chunk_data(png_ptr, (png_bytep)new_name, + png_write_chunk_data(png_ptr, (png_bytep)spalette->name, (png_size_t)(name_len + 1)); png_write_chunk_data(png_ptr, &spalette->depth, (png_size_t)1); @@ -1256,7 +1251,6 @@ png_write_sPLT(png_structp png_ptr, png_const_sPLT_tp spalette) #endif png_write_chunk_end(png_ptr); - png_free(png_ptr, new_name); } #endif @@ -1519,12 +1513,10 @@ png_write_tEXt(png_structp png_ptr, png_const_charp key, png_const_charp text, png_size_t text_len) { png_size_t key_len; - png_charp new_key; png_debug(1, "in png_write_tEXt"); - if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0) - return; + key_len = strlen(key); if (text == NULL || *text == '\0') text_len = 0; @@ -1541,7 +1533,7 @@ png_write_tEXt(png_structp png_ptr, png_const_charp key, png_const_charp text, * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. */ - png_write_chunk_data(png_ptr, (png_bytep)new_key, + png_write_chunk_data(png_ptr, (png_bytep)key, (png_size_t)(key_len + 1)); if (text_len != 0) @@ -1549,7 +1541,6 @@ png_write_tEXt(png_structp png_ptr, png_const_charp key, png_const_charp text, (png_size_t)text_len); png_write_chunk_end(png_ptr); - png_free(png_ptr, new_key); } #endif @@ -1561,7 +1552,6 @@ png_write_zTXt(png_structp png_ptr, png_const_charp key, png_const_charp text, { png_size_t key_len; png_byte buf; - png_charp new_key; compression_state comp; png_debug(1, "in png_write_zTXt"); @@ -1572,16 +1562,11 @@ png_write_zTXt(png_structp png_ptr, png_const_charp key, png_const_charp text, comp.input = NULL; comp.input_len = 0; - if ((key_len = png_check_keyword(png_ptr, key, &new_key)) == 0) - { - png_free(png_ptr, new_key); - return; - } + key_len = strlen(key); if (text == NULL || *text == '\0' || compression==PNG_TEXT_COMPRESSION_NONE) { - png_write_tEXt(png_ptr, new_key, text, (png_size_t)0); - png_free(png_ptr, new_key); + png_write_tEXt(png_ptr, key, text, (png_size_t)0); return; } @@ -1596,11 +1581,9 @@ png_write_zTXt(png_structp png_ptr, png_const_charp key, png_const_charp text, (png_uint_32)(key_len+text_len + 2)); /* Write key */ - png_write_chunk_data(png_ptr, (png_bytep)new_key, + png_write_chunk_data(png_ptr, (png_bytep)key, (png_size_t)(key_len + 1)); - png_free(png_ptr, new_key); - buf = (png_byte)compression; /* Write compression */ @@ -1621,8 +1604,6 @@ png_write_iTXt(png_structp png_ptr, int compression, png_const_charp key, png_const_charp lang, png_const_charp lang_key, png_const_charp text) { png_size_t lang_len, key_len, lang_key_len, text_len; - png_charp new_lang; - png_charp new_key = NULL; png_byte cbuf[2]; compression_state comp; @@ -1633,15 +1614,13 @@ png_write_iTXt(png_structp png_ptr, int compression, png_const_charp key, comp.output_ptr = NULL; comp.input = NULL; - if ((key_len = png_check_keyword(png_ptr, key, &new_key)) == 0) - return; + key_len = png_strlen(key); - if ((lang_len = png_check_keyword(png_ptr, lang, &new_lang)) == 0) - { - png_warning(png_ptr, "Empty language field in iTXt chunk"); - new_lang = NULL; + if (lang == NULL) lang_len = 0; - } + + else + lang_len = png_strlen(lang); if (lang_key == NULL) lang_key_len = 0; @@ -1676,7 +1655,7 @@ png_write_iTXt(png_structp png_ptr, int compression, png_const_charp key, * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. */ - png_write_chunk_data(png_ptr, (png_bytep)new_key, (png_size_t)(key_len + 1)); + png_write_chunk_data(png_ptr, (png_bytep)key, (png_size_t)(key_len + 1)); /* Set the compression flag */ if (compression == PNG_ITXT_COMPRESSION_NONE || @@ -1692,7 +1671,7 @@ png_write_iTXt(png_structp png_ptr, int compression, png_const_charp key, png_write_chunk_data(png_ptr, cbuf, (png_size_t)2); cbuf[0] = 0; - png_write_chunk_data(png_ptr, (new_lang ? (png_const_bytep)new_lang : cbuf), + png_write_chunk_data(png_ptr, (lang ? (png_const_bytep)lang : cbuf), (png_size_t)(lang_len + 1)); png_write_chunk_data(png_ptr, (lang_key ? (png_const_bytep)lang_key : cbuf), @@ -1701,9 +1680,6 @@ png_write_iTXt(png_structp png_ptr, int compression, png_const_charp key, png_write_compressed_data_out(png_ptr, &comp, text_len); png_write_chunk_end(png_ptr); - - png_free(png_ptr, new_key); - png_free(png_ptr, new_lang); } #endif @@ -1737,7 +1713,6 @@ png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, png_size_t purpose_len, units_len, total_len; png_size_tp params_len; png_byte buf[10]; - png_charp new_purpose; int i; png_debug1(1, "in png_write_pCAL (%d parameters)", nparams); @@ -1745,7 +1720,7 @@ png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, if (type >= PNG_EQUATION_LAST) png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); - purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1; + purpose_len = strlen(purpose) + 1; png_debug1(3, "pCAL purpose length = %d", (int)purpose_len); units_len = png_strlen(units) + (nparams == 0 ? 0 : 1); png_debug1(3, "pCAL units length = %d", (int)units_len); @@ -1767,7 +1742,7 @@ png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, png_debug1(3, "pCAL total length = %d", (int)total_len); png_write_chunk_header(png_ptr, png_pCAL, (png_uint_32)total_len); - png_write_chunk_data(png_ptr, (png_const_bytep)new_purpose, purpose_len); + png_write_chunk_data(png_ptr, (png_const_bytep)purpose, purpose_len); png_save_int_32(buf, X0); png_save_int_32(buf + 4, X1); buf[8] = (png_byte)type; @@ -1775,8 +1750,6 @@ png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, png_write_chunk_data(png_ptr, buf, (png_size_t)10); png_write_chunk_data(png_ptr, (png_const_bytep)units, (png_size_t)units_len); - png_free(png_ptr, new_purpose); - for (i = 0; i < nparams; i++) { png_write_chunk_data(png_ptr, (png_const_bytep)params[i], params_len[i]);