diff options
| author | Chong Yidong | 2012-07-03 13:28:42 +0800 |
|---|---|---|
| committer | Chong Yidong | 2012-07-03 13:28:42 +0800 |
| commit | a76e6535dc91d65de27f194861a5aa21e9b26365 (patch) | |
| tree | 0ab4f191fd1a5e6ed1e2582be7f86aa57638440b | |
| parent | 36429c89cbd7282a7614a358e5edb4d37f4a3f47 (diff) | |
| download | emacs-a76e6535dc91d65de27f194861a5aa21e9b26365.tar.gz emacs-a76e6535dc91d65de27f194861a5aa21e9b26365.zip | |
* xml.el: Protect parser against XML bombs.
(xml-entity-expansion-limit): New variable.
(xml-parse-string, xml-substitute-special): Use it.
(xml-parse-dtd): Avoid infloop if the DTD is not terminated.
* test/automated/xml-parse-tests.el: Update testcases.
| -rw-r--r-- | lisp/ChangeLog | 7 | ||||
| -rw-r--r-- | lisp/xml.el | 37 | ||||
| -rw-r--r-- | test/ChangeLog | 4 | ||||
| -rw-r--r-- | test/automated/xml-parse-tests.el | 19 |
4 files changed, 59 insertions, 8 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 58cb7e69688..dd136f5b053 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,10 @@ | |||
| 1 | 2012-07-03 Chong Yidong <cyd@gnu.org> | ||
| 2 | |||
| 3 | * xml.el: Protect parser against XML bombs. | ||
| 4 | (xml-entity-expansion-limit): New variable. | ||
| 5 | (xml-parse-string, xml-substitute-special): Use it. | ||
| 6 | (xml-parse-dtd): Avoid infloop if the DTD is not terminated. | ||
| 7 | |||
| 1 | 2012-07-03 Glenn Morris <rgm@gnu.org> | 8 | 2012-07-03 Glenn Morris <rgm@gnu.org> |
| 2 | 9 | ||
| 3 | * progmodes/bug-reference.el (bug-reference-bug-regexp): | 10 | * progmodes/bug-reference.el (bug-reference-bug-regexp): |
diff --git a/lisp/xml.el b/lisp/xml.el index a3e279b41bd..2595fd572f4 100644 --- a/lisp/xml.el +++ b/lisp/xml.el | |||
| @@ -98,6 +98,13 @@ | |||
| 98 | ("amp" . "&")) | 98 | ("amp" . "&")) |
| 99 | "Alist mapping XML entities to their replacement text.") | 99 | "Alist mapping XML entities to their replacement text.") |
| 100 | 100 | ||
| 101 | (defvar xml-entity-expansion-limit 20000 | ||
| 102 | "The maximum size of entity reference expansions. | ||
| 103 | If the size of the buffer increases by this many characters while | ||
| 104 | expanding entity references in a segment of character data, the | ||
| 105 | XML parser signals an error. Setting this to nil removes the | ||
| 106 | limit (making the parser vulnerable to XML bombs).") | ||
| 107 | |||
| 101 | (defvar xml-parameter-entity-alist nil | 108 | (defvar xml-parameter-entity-alist nil |
| 102 | "Alist of defined XML parametric entities.") | 109 | "Alist of defined XML parametric entities.") |
| 103 | 110 | ||
| @@ -471,7 +478,7 @@ Return one of: | |||
| 471 | (while (not (looking-at end)) | 478 | (while (not (looking-at end)) |
| 472 | (cond | 479 | (cond |
| 473 | ((eobp) | 480 | ((eobp) |
| 474 | (error "XML: (Not Well-Formed) End of buffer while reading element `%s'" | 481 | (error "XML: (Not Well-Formed) End of document while reading element `%s'" |
| 475 | node-name)) | 482 | node-name)) |
| 476 | ((looking-at "</") | 483 | ((looking-at "</") |
| 477 | (forward-char 2) | 484 | (forward-char 2) |
| @@ -517,6 +524,8 @@ Leave point at the start of the next thing to parse. This | |||
| 517 | function can modify the buffer by expanding entity and character | 524 | function can modify the buffer by expanding entity and character |
| 518 | references." | 525 | references." |
| 519 | (let ((start (point)) | 526 | (let ((start (point)) |
| 527 | ;; Keep track of the size of the rest of the buffer: | ||
| 528 | (old-remaining-size (- (buffer-size) (point))) | ||
| 520 | ref val) | 529 | ref val) |
| 521 | (while (and (not (eobp)) | 530 | (while (and (not (eobp)) |
| 522 | (not (looking-at "<"))) | 531 | (not (looking-at "<"))) |
| @@ -557,7 +566,13 @@ references." | |||
| 557 | xml-validating-parser | 566 | xml-validating-parser |
| 558 | (error "XML: (Validity) Undefined entity `%s'" ref)) | 567 | (error "XML: (Validity) Undefined entity `%s'" ref)) |
| 559 | (replace-match (cdr val) t t) | 568 | (replace-match (cdr val) t t) |
| 560 | (goto-char (match-beginning 0)))))) | 569 | (goto-char (match-beginning 0)))) |
| 570 | ;; Check for XML bombs. | ||
| 571 | (and xml-entity-expansion-limit | ||
| 572 | (> (- (buffer-size) (point)) | ||
| 573 | (+ old-remaining-size xml-entity-expansion-limit)) | ||
| 574 | (error "XML: Entity reference expansion \ | ||
| 575 | surpassed `xml-entity-expansion-limit'")))) | ||
| 561 | ;; [2.11] Clean up line breaks. | 576 | ;; [2.11] Clean up line breaks. |
| 562 | (let ((end-marker (point-marker))) | 577 | (let ((end-marker (point-marker))) |
| 563 | (goto-char start) | 578 | (goto-char start) |
| @@ -689,6 +704,8 @@ This follows the rule [28] in the XML specifications." | |||
| 689 | (while (not (looking-at "\\s-*\\]")) | 704 | (while (not (looking-at "\\s-*\\]")) |
| 690 | (skip-syntax-forward " ") | 705 | (skip-syntax-forward " ") |
| 691 | (cond | 706 | (cond |
| 707 | ((eobp) | ||
| 708 | (error "XML: (Well-Formed) End of document while reading DTD")) | ||
| 692 | ;; Element declaration [45]: | 709 | ;; Element declaration [45]: |
| 693 | ((and (looking-at (eval-when-compile | 710 | ((and (looking-at (eval-when-compile |
| 694 | (concat "<!ELEMENT\\s-+\\(" xml-name-re | 711 | (concat "<!ELEMENT\\s-+\\(" xml-name-re |
| @@ -797,9 +814,11 @@ This follows the rule [28] in the XML specifications." | |||
| 797 | (if (re-search-forward parameter-entity-re nil t) | 814 | (if (re-search-forward parameter-entity-re nil t) |
| 798 | (match-beginning 0))))) | 815 | (match-beginning 0))))) |
| 799 | 816 | ||
| 800 | ;; Anything else: | 817 | ;; Anything else is garbage (ignored if not validating). |
| 801 | (xml-validating-parser | 818 | (xml-validating-parser |
| 802 | (error "XML: (Validity) Invalid DTD item")))) | 819 | (error "XML: (Validity) Invalid DTD item")) |
| 820 | (t | ||
| 821 | (skip-chars-forward "^]")))) | ||
| 803 | 822 | ||
| 804 | (if (looking-at "\\s-*]>") | 823 | (if (looking-at "\\s-*]>") |
| 805 | (goto-char (match-end 0)))) | 824 | (goto-char (match-end 0)))) |
| @@ -876,6 +895,7 @@ STRING is assumed to occur in an XML attribute value." | |||
| 876 | (let ((ref-re (eval-when-compile | 895 | (let ((ref-re (eval-when-compile |
| 877 | (concat "&\\(?:#\\(x\\)?\\([0-9]+\\)\\|\\(" | 896 | (concat "&\\(?:#\\(x\\)?\\([0-9]+\\)\\|\\(" |
| 878 | xml-name-re "\\)\\);"))) | 897 | xml-name-re "\\)\\);"))) |
| 898 | (strlen (length string)) | ||
| 879 | children) | 899 | children) |
| 880 | (while (string-match ref-re string) | 900 | (while (string-match ref-re string) |
| 881 | (push (substring string 0 (match-beginning 0)) children) | 901 | (push (substring string 0 (match-beginning 0)) children) |
| @@ -891,7 +911,8 @@ STRING is assumed to occur in an XML attribute value." | |||
| 891 | (error "XML: (Validity) Undefined character `x%s'" ref)) | 911 | (error "XML: (Validity) Undefined character `x%s'" ref)) |
| 892 | (t xml-undefined-entity)) | 912 | (t xml-undefined-entity)) |
| 893 | children) | 913 | children) |
| 894 | (setq string remainder)) | 914 | (setq string remainder |
| 915 | strlen (length string))) | ||
| 895 | ;; [4.4.5] Entity references are "included in literal". | 916 | ;; [4.4.5] Entity references are "included in literal". |
| 896 | ;; Note that we don't need do anything special to treat | 917 | ;; Note that we don't need do anything special to treat |
| 897 | ;; quotes as normal data characters. | 918 | ;; quotes as normal data characters. |
| @@ -900,7 +921,11 @@ STRING is assumed to occur in an XML attribute value." | |||
| 900 | (if xml-validating-parser | 921 | (if xml-validating-parser |
| 901 | (error "XML: (Validity) Undefined entity `%s'" ref) | 922 | (error "XML: (Validity) Undefined entity `%s'" ref) |
| 902 | xml-undefined-entity)))) | 923 | xml-undefined-entity)))) |
| 903 | (setq string (concat val remainder)))))) | 924 | (setq string (concat val remainder))) |
| 925 | (and xml-entity-expansion-limit | ||
| 926 | (> (length string) (+ strlen xml-entity-expansion-limit)) | ||
| 927 | (error "XML: Passed `xml-entity-expansion-limit' while expanding `&%s;'" | ||
| 928 | ref))))) | ||
| 904 | (mapconcat 'identity (nreverse (cons string children)) ""))) | 929 | (mapconcat 'identity (nreverse (cons string children)) ""))) |
| 905 | 930 | ||
| 906 | (defun xml-substitute-numeric-entities (string) | 931 | (defun xml-substitute-numeric-entities (string) |
diff --git a/test/ChangeLog b/test/ChangeLog index 3ff7124893a..1e77f972965 100644 --- a/test/ChangeLog +++ b/test/ChangeLog | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | 2012-07-03 Chong Yidong <cyd@gnu.org> | ||
| 2 | |||
| 3 | * automated/xml-parse-tests.el (xml-parse-tests--bad-data): New. | ||
| 4 | |||
| 1 | 2012-07-02 Chong Yidong <cyd@gnu.org> | 5 | 2012-07-02 Chong Yidong <cyd@gnu.org> |
| 2 | 6 | ||
| 3 | * automated/xml-parse-tests.el (xml-parse-tests--data): More | 7 | * automated/xml-parse-tests.el (xml-parse-tests--data): More |
diff --git a/test/automated/xml-parse-tests.el b/test/automated/xml-parse-tests.el index ec3d7ca3065..ada9bbd4074 100644 --- a/test/automated/xml-parse-tests.el +++ b/test/automated/xml-parse-tests.el | |||
| @@ -55,14 +55,29 @@ | |||
| 55 | ("<foo>&amp;</foo>" . ((foo () "&")))) | 55 | ("<foo>&amp;</foo>" . ((foo () "&")))) |
| 56 | "Alist of XML strings and their expected parse trees.") | 56 | "Alist of XML strings and their expected parse trees.") |
| 57 | 57 | ||
| 58 | (defvar xml-parse-tests--bad-data | ||
| 59 | '(;; XML bomb in content | ||
| 60 | "<!DOCTYPE foo [<!ENTITY lol \"lol\"><!ENTITY lol1 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\"><!ENTITY lol2 \"&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;\">]><foo>&lol2;</foo>" | ||
| 61 | ;; XML bomb in attribute value | ||
| 62 | "<!DOCTYPE foo [<!ENTITY lol \"lol\"><!ENTITY lol1 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\"><!ENTITY lol2 \"&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;\">]><foo a=\"&lol2;\">!</foo>" | ||
| 63 | ;; Non-terminating DTD | ||
| 64 | "<!DOCTYPE foo [ <!ENTITY b \"B\"><!ENTITY abc \"a&b;c\">" | ||
| 65 | "<!DOCTYPE foo [ <!ENTITY b \"B\"><!ENTITY abc \"a&b;c\">asdf" | ||
| 66 | "<!DOCTYPE foo [ <!ENTITY b \"B\"><!ENTITY abc \"a&b;c\">asdf&abc;") | ||
| 67 | "List of XML strings that should signal an error in the parser") | ||
| 68 | |||
| 58 | (ert-deftest xml-parse-tests () | 69 | (ert-deftest xml-parse-tests () |
| 59 | "Test XML parsing." | 70 | "Test XML parsing." |
| 60 | (with-temp-buffer | 71 | (with-temp-buffer |
| 61 | (dolist (test xml-parse-tests--data) | 72 | (dolist (test xml-parse-tests--data) |
| 62 | (erase-buffer) | 73 | (erase-buffer) |
| 63 | (insert (car test)) | 74 | (insert (car test)) |
| 64 | (should (equal (cdr test) | 75 | (should (equal (cdr test) (xml-parse-region)))) |
| 65 | (xml-parse-region (point-min) (point-max))))))) | 76 | (let ((xml-entity-expansion-limit 50)) |
| 77 | (dolist (test xml-parse-tests--bad-data) | ||
| 78 | (erase-buffer) | ||
| 79 | (insert test) | ||
| 80 | (should-error (xml-parse-region)))))) | ||
| 66 | 81 | ||
| 67 | ;; Local Variables: | 82 | ;; Local Variables: |
| 68 | ;; no-byte-compile: t | 83 | ;; no-byte-compile: t |