Browse Source

Applying Debians 12-zero-slot.dpatch by Karl Tomlinson
<karlt@karlt.net> to fix dosfsck zero slot crashes:

* Fixes crashes due to zero slot numbers causing a negative offset in
the call to copy_lfn_part in lfn_add_slot. On amd64 this results in
a SIGSEGV in copy_lfn_part. On x86 the result is heap corruption and
thus sometimes a SIGSEGV or double free abort later. (Closes:
#152550, #353198, #356377, #401798).

Signed-off-by: Daniel Baumann <mail@daniel-baumann.ch>

Daniel Baumann 15 years ago
parent
commit
1b2c8ca3b4
1 changed files with 64 additions and 63 deletions
  1. 64 63
      dosfsck/lfn.c

+ 64 - 63
dosfsck/lfn.c

@@ -148,12 +148,15 @@ void lfn_reset( void )
 void lfn_add_slot( DIR_ENT *de, loff_t dir_offset )
 {
     LFN_ENT *lfn = (LFN_ENT *)de;
+    int slot = lfn->id & LFN_ID_SLOTMASK;
     unsigned offset;
 
+    if (lfn_slot == 0) lfn_check_orphaned();
+
     if (de->attr != VFAT_LN_ATTR)
 	die("lfn_add_slot called with non-LFN directory entry");
 
-    if (lfn->id & LFN_ID_START) {
+    if (lfn->id & LFN_ID_START && slot != 0) {
 	if (lfn_slot != -1) {
 	    int can_clear = 0;
 	    /* There is already a LFN "in progess", so it is an error that a
@@ -165,7 +168,7 @@ void lfn_add_slot( DIR_ENT *de, loff_t dir_offset )
 	    /* XXX: Should delay that until next LFN known (then can better
 	     * display the name) */
 	    printf( "A new long file name starts within an old one.\n" );
-	    if ((lfn->id & LFN_ID_SLOTMASK) == lfn_slot &&
+	    if (slot == lfn_slot &&
 		lfn->alias_checksum == lfn_checksum) {
 		char *part1 = CNV_THIS_PART(lfn);
 		char *part2 = CNV_PARTS_SO_FAR();
@@ -197,13 +200,13 @@ void lfn_add_slot( DIR_ENT *de, loff_t dir_offset )
 		}
 	    }
 	}
-	lfn_slot = lfn->id & LFN_ID_SLOTMASK;
+	lfn_slot = slot;
 	lfn_checksum = lfn->alias_checksum;
 	lfn_unicode = alloc( (lfn_slot*CHARS_PER_LFN+1)*2 );
 	lfn_offsets = alloc( lfn_slot*sizeof(loff_t) );
 	lfn_parts = 0;
     }
-    else if (lfn_slot == -1) {
+    else if (lfn_slot == -1 && slot != 0) {
 	/* No LFN in progress, but slot found; start bit missing */
 	/* Causes: 1) start bit got lost, 2) Previous slot with start bit got
 	 *         lost */
@@ -217,32 +220,30 @@ void lfn_add_slot( DIR_ENT *de, loff_t dir_offset )
 		    "3: Set start bit\n" );
 	}
 	else printf( "  Not auto-correcting this.\n" );
-	if (interactive) {
-	    switch( get_key( "123", "?" )) {
-	      case '1':
-		if (!lfn_offsets)
-		    lfn_offsets = alloc( sizeof(loff_t) );
-		lfn_offsets[0] = dir_offset;
-		clear_lfn_slots( 0, 0 );
-		lfn_reset();
-		return;
-	      case '2':
-		lfn_reset();
-		return;
-	      case '3':
-		lfn->id |= LFN_ID_START;
-		fs_write( dir_offset+offsetof(LFN_ENT,id),
-			  sizeof(lfn->id), &lfn->id );
-		lfn_slot = lfn->id & LFN_ID_SLOTMASK;
-		lfn_checksum = lfn->alias_checksum;
-		lfn_unicode = alloc( (lfn_slot*CHARS_PER_LFN+1)*2 );
-		lfn_offsets = alloc( lfn_slot*sizeof(loff_t) );
-		lfn_parts = 0;
-		break;
-	    }
+	switch( interactive ? get_key( "123", "?" ) : '2') {
+	  case '1':
+	    if (!lfn_offsets)
+	        lfn_offsets = alloc( sizeof(loff_t) );
+	    lfn_offsets[0] = dir_offset;
+	    clear_lfn_slots( 0, 0 );
+	    lfn_reset();
+	    return;
+	  case '2':
+	    lfn_reset();
+	    return;
+	  case '3':
+	    lfn->id |= LFN_ID_START;
+	    fs_write( dir_offset+offsetof(LFN_ENT,id),
+		      sizeof(lfn->id), &lfn->id );
+	    lfn_slot = slot;
+	    lfn_checksum = lfn->alias_checksum;
+	    lfn_unicode = alloc( (lfn_slot*CHARS_PER_LFN+1)*2 );
+	    lfn_offsets = alloc( lfn_slot*sizeof(loff_t) );
+	    lfn_parts = 0;
+	    break;
 	}
     }
-    else if ((lfn->id & LFN_ID_SLOTMASK) != lfn_slot) {
+    else if (slot != lfn_slot) {
 	/* wrong sequence number */
 	/* Causes: 1) seq-no destroyed */
 	/* Fixes: 1) delete LFN, 2) fix number (maybe only if following parts
@@ -251,8 +252,8 @@ void lfn_add_slot( DIR_ENT *de, loff_t dir_offset )
 	int can_fix = 0;
 	printf( "Unexpected long filename sequence number "
 		"(%d vs. expected %d).\n",
-		(lfn->id & LFN_ID_SLOTMASK), lfn_slot );
-	if (lfn->alias_checksum == lfn_checksum) {
+		slot, lfn_slot );
+	if (lfn->alias_checksum == lfn_checksum && lfn_slot > 0) {
 	    char *part1 = CNV_THIS_PART(lfn);
 	    char *part2 = CNV_PARTS_SO_FAR();
 	    printf( "  It could be that just the number is wrong\n"
@@ -267,22 +268,24 @@ void lfn_add_slot( DIR_ENT *de, loff_t dir_offset )
 		printf( "3: Correct sequence number\n" );
 	}
 	else printf( "  Not auto-correcting this.\n" );
-	if (interactive) {
-	    switch( get_key( can_fix ? "123" : "12", "?" )) {
-	      case '1':
-		lfn_offsets[lfn_parts++] = dir_offset;
-		clear_lfn_slots( 0, lfn_parts-1 );
-		lfn_reset();
-		return;
-	      case '2':
-		lfn_reset();
-		return;
-	      case '3':
-		lfn->id = (lfn->id & ~LFN_ID_SLOTMASK) | lfn_slot;
-		fs_write( dir_offset+offsetof(LFN_ENT,id),
-			  sizeof(lfn->id), &lfn->id );
-		break;
+	switch( interactive ? get_key( can_fix ? "123" : "12", "?" ) : '2') {
+	  case '1':
+	    if (!lfn_offsets) {
+	        lfn_offsets = alloc( sizeof(loff_t) );
+		lfn_parts = 0;
 	    }
+	    lfn_offsets[lfn_parts++] = dir_offset;
+	    clear_lfn_slots( 0, lfn_parts-1 );
+	    lfn_reset();
+	    return;
+	  case '2':
+	    lfn_reset();
+	    return;
+	  case '3':
+	    lfn->id = (lfn->id & ~LFN_ID_SLOTMASK) | lfn_slot;
+	    fs_write( dir_offset+offsetof(LFN_ENT,id),
+		      sizeof(lfn->id), &lfn->id );
+	    break;
 	}
     }
 
@@ -390,25 +393,23 @@ char *lfn_get( DIR_ENT *de )
 		    "it to short name %s)\n", short_name );
 	}
 	else printf( "  Not auto-correcting this.\n" );
-	if (interactive) {
-	    switch( get_key( "123", "?" )) {
-	      case '1':
-		clear_lfn_slots( 0, lfn_parts-1 );
-		lfn_reset();
-		return NULL;
-	      case '2':
-		lfn_reset();
-		return NULL;
-	      case '3':
-		for( i = 0; i < lfn_parts; ++i ) {
-		    __u8 id = (lfn_parts-i) | (i==0 ? LFN_ID_START : 0);
-		    fs_write( lfn_offsets[i]+offsetof(LFN_ENT,id),
-			      sizeof(id), &id );
-		}
-		memmove( lfn_unicode, lfn_unicode+lfn_slot*CHARS_PER_LFN*2,
-			 lfn_parts*CHARS_PER_LFN*2 );
-		break;
+	switch( interactive ? get_key( "123", "?" ) : '2') {
+	  case '1':
+	    clear_lfn_slots( 0, lfn_parts-1 );
+	    lfn_reset();
+	    return NULL;
+	  case '2':
+	    lfn_reset();
+	    return NULL;
+	  case '3':
+	    for( i = 0; i < lfn_parts; ++i ) {
+	        __u8 id = (lfn_parts-i) | (i==0 ? LFN_ID_START : 0);
+		fs_write( lfn_offsets[i]+offsetof(LFN_ENT,id),
+			  sizeof(id), &id );
 	    }
+	    memmove( lfn_unicode, lfn_unicode+lfn_slot*CHARS_PER_LFN*2,
+		     lfn_parts*CHARS_PER_LFN*2 );
+	    break;
 	}
     }