summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2026-01-22 13:12:05 +0100
committerGravatar Nikias Bassen2026-01-22 13:12:05 +0100
commitac7b64160f5c204b11451cdda0165e0a1a810188 (patch)
treeb08f887081b773a297f04b3f61e3277c925195c7 /src
parent502eb2a10201e98f247186672add810e54afc15b (diff)
downloadlibplist-ac7b64160f5c204b11451cdda0165e0a1a810188.tar.gz
libplist-ac7b64160f5c204b11451cdda0165e0a1a810188.tar.bz2
xplist: Harden entity unescaping against malformed input
- Fix numeric character reference parsing - Enforce exact entity name matching - Guard against size_t underflow and oversized entities - Reject invalid Unicode code points
Diffstat (limited to 'src')
-rw-r--r--src/xplist.c50
1 files changed, 32 insertions, 18 deletions
diff --git a/src/xplist.c b/src/xplist.c
index 9870104..55b01e9 100644
--- a/src/xplist.c
+++ b/src/xplist.c
@@ -853,42 +853,50 @@ static int unescape_entities(char *str, size_t *length)
853 PLIST_XML_ERR("Invalid entity sequence encountered (missing terminating ';')\n"); 853 PLIST_XML_ERR("Invalid entity sequence encountered (missing terminating ';')\n");
854 return -1; 854 return -1;
855 } 855 }
856 if (str+i >= entp+1) { 856 size_t entlen = (size_t)(str+i - entp);
857 int entlen = str+i - entp; 857 if (entlen > 0) {
858 int bytelen = 1; 858 if (entlen > 256) {
859 if (!strncmp(entp, "amp", 3)) { 859 PLIST_XML_ERR("Rejecting absurdly large entity at &%.*s...\n", 8, entp);
860 return -1;
861 }
862 size_t bytelen = 1;
863 if (entlen == 3 && memcmp(entp, "amp", 3) == 0) {
860 /* the '&' is already there */ 864 /* the '&' is already there */
861 } else if (!strncmp(entp, "apos", 4)) { 865 } else if (entlen == 4 && memcmp(entp, "apos", 4) == 0) {
862 *(entp-1) = '\''; 866 *(entp-1) = '\'';
863 } else if (!strncmp(entp, "quot", 4)) { 867 } else if (entlen == 4 && memcmp(entp, "quot", 4) == 0) {
864 *(entp-1) = '"'; 868 *(entp-1) = '"';
865 } else if (!strncmp(entp, "lt", 2)) { 869 } else if (entlen == 2 && memcmp(entp, "lt", 2) == 0) {
866 *(entp-1) = '<'; 870 *(entp-1) = '<';
867 } else if (!strncmp(entp, "gt", 2)) { 871 } else if (entlen == 2 && memcmp(entp, "gt", 2) == 0) {
868 *(entp-1) = '>'; 872 *(entp-1) = '>';
869 } else if (*entp == '#') { 873 } else if (*entp == '#') {
870 /* numerical character reference */ 874 /* numerical character reference */
871 uint64_t val = 0; 875 uint64_t val = 0;
872 char* ep = NULL; 876 char* ep = NULL;
873 if (entlen > 8) { 877 if (entlen > 8) {
874 PLIST_XML_ERR("Invalid numerical character reference encountered, sequence too long: &%.*s;\n", entlen, entp); 878 PLIST_XML_ERR("Invalid numerical character reference encountered, sequence too long: &%.*s;\n", (int)entlen, entp);
875 return -1; 879 return -1;
876 } 880 }
877 if (*(entp+1) == 'x' || *(entp+1) == 'X') { 881 if (entlen >= 2 && (entp[1] == 'x' || entp[1] == 'X')) {
878 if (entlen < 3) { 882 if (entlen < 3) {
879 PLIST_XML_ERR("Invalid numerical character reference encountered, sequence too short: &%.*s;\n", entlen, entp); 883 PLIST_XML_ERR("Invalid numerical character reference encountered, sequence too short: &%.*s;\n", (int)entlen, entp);
880 return -1; 884 return -1;
881 } 885 }
882 val = strtoull(entp+2, &ep, 16); 886 val = strtoull(entp+2, &ep, 16);
883 } else { 887 } else {
884 if (entlen < 2) { 888 if (entlen < 2) {
885 PLIST_XML_ERR("Invalid numerical character reference encountered, sequence too short: &%.*s;\n", entlen, entp); 889 PLIST_XML_ERR("Invalid numerical character reference encountered, sequence too short: &%.*s;\n", (int)entlen, entp);
886 return -1; 890 return -1;
887 } 891 }
888 val = strtoull(entp+1, &ep, 10); 892 val = strtoull(entp+1, &ep, 10);
889 } 893 }
890 if (val == 0 || val > 0x10FFFF || ep-entp != entlen) { 894 if (val == 0 || val > 0x10FFFF || (size_t)(ep-entp) != entlen) {
891 PLIST_XML_ERR("Invalid numerical character reference found: &%.*s;\n", entlen, entp); 895 PLIST_XML_ERR("Invalid numerical character reference found: &%.*s;\n", (int)entlen, entp);
896 return -1;
897 }
898 if (val >= 0xD800 && val <= 0xDFFF) {
899 PLIST_XML_ERR("Invalid numerical character reference found (surrogate): &%.*s;\n", (int)entlen, entp);
892 return -1; 900 return -1;
893 } 901 }
894 /* convert to UTF8 */ 902 /* convert to UTF8 */
@@ -918,12 +926,18 @@ static int unescape_entities(char *str, size_t *length)
918 *(entp-1) = (char)(val & 0x7F); 926 *(entp-1) = (char)(val & 0x7F);
919 } 927 }
920 } else { 928 } else {
921 PLIST_XML_ERR("Invalid entity encountered: &%.*s;\n", entlen, entp); 929 PLIST_XML_ERR("Invalid entity encountered: &%.*s;\n", (int)entlen, entp);
930 return -1;
931 }
932 memmove(entp, str+i+1, len - i); /* include '\0' */
933 size_t dec = entlen + 1 - bytelen;
934 size_t shrink = entlen + 2 - bytelen;
935 if (i < dec || len < shrink) {
936 PLIST_XML_ERR("Internal error: length underflow?!\n");
922 return -1; 937 return -1;
923 } 938 }
924 memmove(entp, str+i+1, len - i); 939 i -= dec;
925 i -= entlen+1 - bytelen; 940 len -= shrink;
926 len -= entlen+2 - bytelen;
927 continue; 941 continue;
928 } else { 942 } else {
929 PLIST_XML_ERR("Invalid empty entity sequence &;\n"); 943 PLIST_XML_ERR("Invalid empty entity sequence &;\n");