Skip to content

Commit a62d1b0

Browse files
nedbassbehlendorf
authored andcommitted
Fix SA header size accounting
The functions sa_find_sizes() and sa_build_layout() fail to account for the additional 2 bytes of SA header space when calculating whether a variable size attribute might spill over. They may consequently determine that an attribute will fit in the bonus buffer along with a spill block pointer, when in reality the attribute would be partially overwritten by the spill block pointer if spill over occurs. This also causes an inconsistency between the SA header size and the number of variable size attributes in the layout, tripping an assertion when debugging is on. The following reproducer demonstrates the problem. ln -s $(perl -e 'print "z" x 20') file setfattr -h -n trusted.foo -v $(perl -e 'print "z" x 200') file Even though sa_find_sizes() computes the index of the attribute where spill-over will occur, sa_build_layouts() discards the result and recomputes it itself. As it turns out, both functions get it wrong. Since this computation is awkward and, as history has shown, easy to screw up, let's just do it in one place. This patch fixes the bug in sa_find_sizes() and updates sa_build_layout() to use the result computed there. Also improve the comments in sa_find_sizes(). Signed-off-by: Ned Bass <bass6@llnl.gov> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Tim Chase <tim@chase2k.com> Closes #3070
1 parent e2c4acd commit a62d1b0

File tree

1 file changed

+44
-41
lines changed

1 file changed

+44
-41
lines changed

module/zfs/sa.c

Lines changed: 44 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -538,20 +538,30 @@ sa_copy_data(sa_data_locator_t *func, void *datastart, void *target, int buflen)
538538
}
539539

540540
/*
541-
* Determine several different sizes
542-
* first the sa header size
543-
* the number of bytes to be stored
544-
* if spill would occur the index in the attribute array is returned
541+
* Determine several different values pertaining to system attribute
542+
* buffers.
545543
*
546-
* the boolean will_spill will be set when spilling is necessary. It
547-
* is only set when the buftype is SA_BONUS
544+
* Return the size of the sa_hdr_phys_t header for the buffer. Each
545+
* variable length attribute except the first contributes two bytes to
546+
* the header size, which is then rounded up to an 8-byte boundary.
547+
*
548+
* The following output parameters are also computed.
549+
*
550+
* index - The index of the first attribute in attr_desc that will
551+
* spill over. Only valid if will_spill is set.
552+
*
553+
* total - The total number of bytes of all system attributes described
554+
* in attr_desc.
555+
*
556+
* will_spill - Set when spilling is necessary. It is only set when
557+
* the buftype is SA_BONUS.
548558
*/
549559
static int
550560
sa_find_sizes(sa_os_t *sa, sa_bulk_attr_t *attr_desc, int attr_count,
551561
dmu_buf_t *db, sa_buf_type_t buftype, int *index, int *total,
552562
boolean_t *will_spill)
553563
{
554-
int var_size = 0;
564+
int var_size_count = 0;
555565
int i;
556566
int full_space;
557567
int hdrsize;
@@ -577,38 +587,43 @@ sa_find_sizes(sa_os_t *sa, sa_bulk_attr_t *attr_desc, int attr_count,
577587

578588
for (i = 0; i != attr_count; i++) {
579589
boolean_t is_var_sz, might_spill_here;
590+
int tmp_hdrsize;
580591

581592
*total = P2ROUNDUP(*total, 8);
582593
*total += attr_desc[i].sa_length;
583594
if (*will_spill)
584595
continue;
585596

586597
is_var_sz = (SA_REGISTERED_LEN(sa, attr_desc[i].sa_attr) == 0);
587-
if (is_var_sz) {
588-
var_size++;
589-
}
598+
if (is_var_sz)
599+
var_size_count++;
600+
601+
/*
602+
* Calculate what the SA header size would be if this
603+
* attribute doesn't spill.
604+
*/
605+
tmp_hdrsize = hdrsize + ((is_var_sz && var_size_count > 1) ?
606+
sizeof (uint16_t) : 0);
590607

608+
/*
609+
* Check whether this attribute spans into the space
610+
* that would be used by the spill block pointer should
611+
* a spill block be needed.
612+
*/
591613
might_spill_here =
592614
buftype == SA_BONUS && *index == -1 &&
593-
(*total + P2ROUNDUP(hdrsize, 8)) >
615+
(*total + P2ROUNDUP(tmp_hdrsize, 8)) >
594616
(full_space - sizeof (blkptr_t));
595617

596-
if (is_var_sz && var_size > 1) {
597-
/*
598-
* Don't worry that the spill block might overflow.
599-
* It will be resized if needed in sa_build_layouts().
600-
*/
618+
if (is_var_sz && var_size_count > 1) {
601619
if (buftype == SA_SPILL ||
602-
P2ROUNDUP(hdrsize + sizeof (uint16_t), 8) +
603-
*total < full_space) {
620+
tmp_hdrsize + *total < full_space) {
604621
/*
605-
* Account for header space used by array of
606-
* optional sizes of variable-length attributes.
607622
* Record the extra header size in case this
608623
* increase needs to be reversed due to
609624
* spill-over.
610625
*/
611-
hdrsize += sizeof (uint16_t);
626+
hdrsize = tmp_hdrsize;
612627
if (*index != -1 || might_spill_here)
613628
extra_hdrsize += sizeof (uint16_t);
614629
} else {
@@ -621,10 +636,9 @@ sa_find_sizes(sa_os_t *sa, sa_bulk_attr_t *attr_desc, int attr_count,
621636
}
622637

623638
/*
624-
* find index of where spill *could* occur.
625-
* Then continue to count of remainder attribute
626-
* space. The sum is used later for sizing bonus
627-
* and spill buffer.
639+
* Store index of where spill *could* occur. Then
640+
* continue to count the remaining attribute sizes. The
641+
* sum is used later for sizing bonus and spill buffer.
628642
*/
629643
if (might_spill_here)
630644
*index = i;
@@ -657,9 +671,9 @@ sa_build_layouts(sa_handle_t *hdl, sa_bulk_attr_t *attr_desc, int attr_count,
657671
sa_buf_type_t buftype;
658672
sa_hdr_phys_t *sahdr;
659673
void *data_start;
660-
int buf_space;
661674
sa_attr_type_t *attrs, *attrs_start;
662675
int i, lot_count;
676+
int spill_idx;
663677
int hdrsize;
664678
int spillhdrsize = 0;
665679
int used;
@@ -674,7 +688,7 @@ sa_build_layouts(sa_handle_t *hdl, sa_bulk_attr_t *attr_desc, int attr_count,
674688

675689
/* first determine bonus header size and sum of all attributes */
676690
hdrsize = sa_find_sizes(sa, attr_desc, attr_count, hdl->sa_bonus,
677-
SA_BONUS, &i, &used, &spilling);
691+
SA_BONUS, &spill_idx, &used, &spilling);
678692

679693
if (used > SPA_MAXBLOCKSIZE)
680694
return (SET_ERROR(EFBIG));
@@ -696,14 +710,13 @@ sa_build_layouts(sa_handle_t *hdl, sa_bulk_attr_t *attr_desc, int attr_count,
696710
}
697711
dmu_buf_will_dirty(hdl->sa_spill, tx);
698712

699-
spillhdrsize = sa_find_sizes(sa, &attr_desc[i],
700-
attr_count - i, hdl->sa_spill, SA_SPILL, &i,
713+
spillhdrsize = sa_find_sizes(sa, &attr_desc[spill_idx],
714+
attr_count - spill_idx, hdl->sa_spill, SA_SPILL, &i,
701715
&spill_used, &dummy);
702716

703717
if (spill_used > SPA_MAXBLOCKSIZE)
704718
return (SET_ERROR(EFBIG));
705719

706-
buf_space = hdl->sa_spill->db_size - spillhdrsize;
707720
if (BUF_SPACE_NEEDED(spill_used, spillhdrsize) >
708721
hdl->sa_spill->db_size)
709722
VERIFY(0 == sa_resize_spill(hdl,
@@ -715,12 +728,6 @@ sa_build_layouts(sa_handle_t *hdl, sa_bulk_attr_t *attr_desc, int attr_count,
715728
sahdr = (sa_hdr_phys_t *)hdl->sa_bonus->db_data;
716729
buftype = SA_BONUS;
717730

718-
if (spilling)
719-
buf_space = (sa->sa_force_spill) ?
720-
0 : SA_BLKPTR_SPACE - hdrsize;
721-
else
722-
buf_space = hdl->sa_bonus->db_size - hdrsize;
723-
724731
attrs_start = attrs = kmem_alloc(sizeof (sa_attr_type_t) * attr_count,
725732
KM_SLEEP);
726733
lot_count = 0;
@@ -729,14 +736,12 @@ sa_build_layouts(sa_handle_t *hdl, sa_bulk_attr_t *attr_desc, int attr_count,
729736
uint16_t length;
730737

731738
ASSERT(IS_P2ALIGNED(data_start, 8));
732-
ASSERT(IS_P2ALIGNED(buf_space, 8));
733739
attrs[i] = attr_desc[i].sa_attr;
734740
length = SA_REGISTERED_LEN(sa, attrs[i]);
735741
if (length == 0)
736742
length = attr_desc[i].sa_length;
737743

738-
if (buf_space < length) { /* switch to spill buffer */
739-
VERIFY(spilling);
744+
if (spilling && i == spill_idx) { /* switch to spill buffer */
740745
VERIFY(bonustype == DMU_OT_SA);
741746
if (buftype == SA_BONUS && !sa->sa_force_spill) {
742747
sa_find_layout(hdl->sa_os, hash, attrs_start,
@@ -753,7 +758,6 @@ sa_build_layouts(sa_handle_t *hdl, sa_bulk_attr_t *attr_desc, int attr_count,
753758
data_start = (void *)((uintptr_t)sahdr +
754759
spillhdrsize);
755760
attrs_start = &attrs[i];
756-
buf_space = hdl->sa_spill->db_size - spillhdrsize;
757761
lot_count = 0;
758762
}
759763
hash ^= SA_ATTR_HASH(attrs[i]);
@@ -766,7 +770,6 @@ sa_build_layouts(sa_handle_t *hdl, sa_bulk_attr_t *attr_desc, int attr_count,
766770
}
767771
data_start = (void *)P2ROUNDUP(((uintptr_t)data_start +
768772
length), 8);
769-
buf_space -= P2ROUNDUP(length, 8);
770773
lot_count++;
771774
}
772775

0 commit comments

Comments
 (0)