Correctly parse multiple tags in the divelog

The old parsing code overwrote the first comma with a '\0' and then
checked the index against the length of the buffer - which was changed by
replacing the ',' with the '\0'.

This means that since commit 78acf20848 ("Don't crash on loading tags
longer than 127 chars") Subsurface has potentially damaged / lost data in
dive files!

Added a test dive that shows the issue if opened by a Subsurface version
after the commit mentioned above but before this commit.

Reported-by: Miika Turkia <miika.turkia@gmail.com>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
Dirk Hohndel 2013-12-12 16:12:04 -08:00
parent 007aa79fd7
commit 89a58e23e0
2 changed files with 72 additions and 12 deletions

62
dives/test29.xml Normal file
View file

@ -0,0 +1,62 @@
<divelog program='subsurface' version='2'>
<settings>
</settings>
<dives>
<dive number='29' tags='boat, wreck' date='2011-12-13' time='06:35:00' duration='30:00 min'>
<location>irrelevant dive location</location>
<notes>This is a dive that has two tags, boat and wreck</notes>
<divecomputer model='Model Product' deviceid='e04d0248' diveid='00000002'>
<depth max='20.1 m' mean='18.293 m' />
<temperature water='20.0 C' />
<sample time='0:06 min' depth='1.86 m' temp='20.0 C' ndl='99:00 min' />
<sample time='1:00 min' depth='20.1 m' />
<sample time='27:00 min' depth='20.1 m' />
<sample time='28:00 min' depth='3.0 m' />
<sample time='29:00 min' depth='3.0 m' />
<sample time='30:00 min' depth='0.3 m' />
</divecomputer>
</dive>
<dive number='30' tags='' date='2011-12-13' time='07:35:00' duration='30:00 min'>
<location>irrelevant dive location</location>
<notes>This is a dive that has an empty tags property in the XML file</notes>
<divecomputer model='Model Product' deviceid='e04d0248' diveid='00000002'>
<depth max='20.1 m' mean='18.293 m' />
<temperature water='20.0 C' />
<sample time='0:06 min' depth='1.86 m' temp='20.0 C' ndl='99:00 min' />
<sample time='1:00 min' depth='20.1 m' />
<sample time='27:00 min' depth='20.1 m' />
<sample time='28:00 min' depth='3.0 m' />
<sample time='29:00 min' depth='3.0 m' />
<sample time='30:00 min' depth='0.3 m' />
</divecomputer>
</dive>
<dive number='31' tags=',' date='2011-12-13' time='08:35:00' duration='30:00 min'>
<location>irrelevant dive location</location>
<notes>This is a dive that has a tags property with just a comma in the XML file</notes>
<divecomputer model='Model Product' deviceid='e04d0248' diveid='00000002'>
<depth max='20.1 m' mean='18.293 m' />
<temperature water='20.0 C' />
<sample time='0:06 min' depth='1.86 m' temp='20.0 C' ndl='99:00 min' />
<sample time='1:00 min' depth='20.1 m' />
<sample time='27:00 min' depth='20.1 m' />
<sample time='28:00 min' depth='3.0 m' />
<sample time='29:00 min' depth='3.0 m' />
<sample time='30:00 min' depth='0.3 m' />
</divecomputer>
</dive>
<dive number='32' tags='a, boat' date='2011-12-13' time='09:35:00' duration='30:00 min'>
<location>irrelevant dive location</location>
<notes>This is a dive that has a single letter tag 'a' (plus the boat tag)</notes>
<divecomputer model='Model Product' deviceid='e04d0248' diveid='00000002'>
<depth max='20.1 m' mean='18.293 m' />
<temperature water='20.0 C' />
<sample time='0:06 min' depth='1.86 m' temp='20.0 C' ndl='99:00 min' />
<sample time='1:00 min' depth='20.1 m' />
<sample time='27:00 min' depth='20.1 m' />
<sample time='28:00 min' depth='3.0 m' />
<sample time='29:00 min' depth='3.0 m' />
<sample time='30:00 min' depth='0.3 m' />
</divecomputer>
</dive>
</dives>
</divelog>

View file

@ -220,19 +220,18 @@ static void divetags(char *buffer, void *_tags)
struct tag_entry *tags = _tags; struct tag_entry *tags = _tags;
int i = 0, start = 0, end = 0; int i = 0, start = 0, end = 0;
enum ParseState state = FINDEND; enum ParseState state = FINDEND;
i=0; int len = buffer ? strlen(buffer) : 0;
while(i < strlen(buffer)) {
while(i < len) {
if (buffer[i] == ',') { if (buffer[i] == ',') {
if (state == FINDSTART) { if (state == FINDSTART) {
/* Detect empty tags */ /* Detect empty tags */
} else if (state == FINDEND) { } else if (state == FINDEND) {
/* Found end of tag */ /* Found end of tag */
if (i > 1) { if (i > 0 && buffer[i - 1] != '\\') {
if(buffer[i-1] != '\\') { buffer[i] = '\0';
buffer[end-start+1] = '\0';
state=FINDSTART; state=FINDSTART;
taglist_add_tag(tags, buffer+start); taglist_add_tag(tags, buffer+start);
}
} else { } else {
state=FINDSTART; state=FINDSTART;
} }
@ -245,18 +244,17 @@ static void divetags(char *buffer, void *_tags)
state = FINDEND; state = FINDEND;
start = i; start = i;
} else if (state == FINDEND) { } else if (state == FINDEND) {
end=i; end = i;
} }
} }
i++; i++;
} }
if (state == FINDEND) { if (state == FINDEND) {
if (end < start) if (end < start)
end = strlen(buffer)-1; end = len - 1;
if (strlen(buffer) > 0) { if (len > 0) {
buffer[end-start+1] = '\0'; buffer[end + 1] = '\0';
state=FINDSTART; taglist_add_tag(tags, buffer + start);
taglist_add_tag(tags, buffer+start);
} }
} }
} }