Repository navigation
Windows: escape the labels of favorite volumes in the favorites file - #1946
Conversation
SaveFavoriteVolumes wrote the label attribute as is, and the reader takes the first '>' for the end of the element. After a restart, a favorite volume whose label contained '<' or '>' lost its path, its label and its options: with the label "A > B", the path was read as ' B" readonly="1">' followed by the actual path, and with "Old <2024>" it was read as empty. The Organize Favorite Volumes dialog already replaces '"' in labels with "'". Escape the label with XmlQuoteTextW, as the path is, and decode "<", ">" and "&" when the file is read, since XmlGetAttributeText returns attribute values as written. Files written by earlier versions load as before, including labels with a '&'.
idrassi
left a comment
There was a problem hiding this comment.
Thanks for tracking this down. The escaping fixes the reported parsing problem, but I found two cases that need addressing before we merge.
The first concerns existing favorites files. A label such as x < y, saved by an older version, becomes x < y when loaded with this change. Older versions wrote labels verbatim, so applying UnquoteXmlText unconditionally changes labels that previously worked. The round trip test covers the new writer followed by the new reader; it doesn't cover loading a file written by an older version. Please preserve the interpretation of existing labels. An explicit encoding marker for newly written labels would let the reader distinguish them without guessing.
There's also a buffer size problem. A label containing 205 ampersands expands to 1025 bytes, which exceeds the reader's 1024 byte buffer. XmlGetAttributeText fails before decoding, and the label comes back empty. With 410 ampersands, the escaped text exceeds the writer's 2048 character buffer, and the label attribute is silently omitted. Both inputs fit within the length currently accepted by the dialog.
Please account for the expanded representation in the writer and reader, and don't silently discard the label if escaping fails.
These cases just need to be handled so existing favorites don't lose their labels after an upgrade.
…limits Earlier versions wrote labels as is, so decoding every label changed those that contain text such as "<". A label that escaping changes is now marked with labelEscaped="1", and only marked labels are decoded. Labels without '&', '<' and '>' are written as before. Escaping makes a label up to five times longer. The writer escaped it into 2048 characters and left the label out when it did not fit, and the reader read it into 1024 bytes and returned it empty when it did not fit. The label is now escaped into a string without a limit and read into a buffer as long as the start tag that holds it.
|
Thanks for the careful review, both points are right. I reproduced them with the first revision: a label written by an earlier version as ef7706a changes this:
One limit remains with earlier versions reading a file written by this one: they ignore the marker, so they show escaped labels with the escapes, and they still cannot read a label longer than 1023 bytes. If such a version saves the file, the escaped text becomes the label. Checked on Windows 11 with this build: a favorites file edited to |
idrassi
left a comment
There was a problem hiding this comment.
Thanks for the quick follow up.
I have left three inline comments.
| s += L" label=\"" + quotedLabel + L"\""; | ||
|
|
||
| if (quotedLabel != favorite.Label) | ||
| s += L" labelEscaped=\"1\""; |
There was a problem hiding this comment.
Could you put the marker before the label, and preferably write the label after the other attributes? XmlGetAttributeText stops at the first matching text even when it sits inside another attribute value. With the label R&D labelEscaped=, this code writes:
label="R&D labelEscaped=" labelEscaped="1"The reader treats the text inside label as the marker. Since that value doesn't fit in the two byte buffer, the marker stays unset and the label is read as R&D labelEscaped=. Each later save adds another amp;. I reproduced this with both ordinary favorites and system favorites.
Writing the real attributes first and label last fixes this case with the current reader. It also keeps a label such as Archive readonly= from hiding the real readonly="1" attribute. That second case currently reloads as writable, so the order matters beyond the new marker.
| /* The label is read into a buffer as long as the start tag that holds it, so that | ||
| it fits however much escaping lengthens it. Earlier versions wrote labels as is, | ||
| so only a label marked with labelEscaped is decoded. */ | ||
| const char *tagEnd = strchr (xml, '>'); |
There was a problem hiding this comment.
Using the first > here means the new reader still can't recover affected files that an older VeraCrypt has already written. At this head, an unmarked A > B label still moves part of the tag into the path and clears readonly. Old <2024> still comes back empty.
Could this locate the closing > outside quoted values? The Unix parser already has FindTagEnd for this. Keeping an unmarked label undecoded would still preserve the older x < y behavior that the marker is meant to protect.
XmlGetAttributeText takes the first match of an attribute name, even inside a value, so a label such as "R&D labelEscaped=" hid the marker and one such as "Archive readonly=" hid the read-only option. The marker and the label are now written after all other attributes, the marker first. XmlGetAttributeText and XmlGetNodeText took the first '>' for the end of the start tag, so favorites that earlier versions wrote with '<' or '>' in the label still lost their path and options. XmlFindTagEnd skips '>' inside attribute values, and the text of a node starts after that end. Unmarked labels stay undecoded. The translators attribute of the Hungarian language file, which contains '>', is now read as well. A label longer than the 1023 UTF-16 units the Organize Favorite Volumes dialog keeps is rejected with its favorite before Utf8StringToWide, whose fixed buffer it could exceed, and the favorites around it load as usual.
|
Thanks, all three are addressed in 7e11f86.
One case remains for files written by earlier versions, where the label comes before the other attributes: a label such as Checked on Windows 11 with this build: |
idrassi
left a comment
There was a problem hiding this comment.
Thanks for the update. Looks good to me now.
SaveFavoriteVolumeswrites the label of a favorite volume into an attribute as is, while the path is escaped withXmlQuoteTextW.XmlGetAttributeTextandXmlGetNodeTexttake the first>for the end of the element, so after a restart a favorite volume whose label contains<or>loses its path, label and options:A > B, the path is read asB" readonly="1">C:\Vols\work.hc, and the label and the read-only option are empty;Old <2024>, the path is read as empty.This PR:
&,<or>escaped and marked withlabelEscaped="1", and decodes only marked labels. Earlier versions wrote labels as is, and their labels, which have no marker, are read as before, including text such as<. Labels without these characters are written exactly as before;XmlFindTagEnd, which skips>inside attribute values, and uses it inXmlGetAttributeTextandXmlGetNodeText, so that favorites written by earlier versions with<or>in a label load with their label, path and options. Thetranslatorsattribute of the Hungarian language file, which contains>, is now read as well;An earlier version reading a file written by this one ignores the marker and shows escaped labels with the escapes, such as
R&D. If it saves the file, the escaped text becomes the label.Testing
Xml.cand the label code ofSaveFavoriteVolumesandLoadFavoriteVolumesfrom this branch, built on Linux:Work,A > B,Old <2024>,R&D,x < y,R&D labelEscaped=,Archive readonly=, 205, 410 and 1023&, and 1023 Cyrillic letters all come back unchanged, read-only.A > BandOld <2024>now load with label, path and read-only option. The other labels load as master loads them, except that 1023 Cyrillic letters come back in full instead of empty.Utf8StringToWideis never given more than 5,115 units, well below its limit.Xml.cfunctions, except the Hungariantranslatorsattribute.Windows 11, with this build:
R&D labelEscaped=stays the same after a restart and another save,Archive readonly=keeps the read-only option, favorites written as earlier versions do withA > BorOld <2024>in the label load with their label, path and read-only option,A > B <2024> & cowith the read-only option is kept, and the language dialog shows the Hungarian translators.