diff options
Diffstat (limited to 'epan')
-rw-r--r-- | epan/exceptions.h | 28 | ||||
-rw-r--r-- | epan/reassemble.c | 81 | ||||
-rw-r--r-- | epan/show_exception.c | 17 |
3 files changed, 90 insertions, 36 deletions
diff --git a/epan/exceptions.h b/epan/exceptions.h index 87431a9bf0..938115e622 100644 --- a/epan/exceptions.h +++ b/epan/exceptions.h @@ -95,6 +95,15 @@ **/ #define OutOfMemoryError 6 +/** + The reassembly state machine was passed a bad fragment offset, + or other similar issues. We used to use DissectorError in these + cases, but they're not necessarily the dissector's fault - if the packet + contains a bad fragment offset, the dissector shouldn't have to figure + that out by itself since that's what the reassembly machine is for. +**/ +#define ReassemblyError 7 + /* * Catch errors that, if you're calling a subdissector and catching * exceptions from the subdissector, and possibly dissecting more @@ -114,7 +123,7 @@ * separately. */ #define CATCH_NONFATAL_ERRORS \ - CATCH2(ReportedBoundsError, ScsiBoundsError) + CATCH3(ReportedBoundsError, ScsiBoundsError, ReassemblyError) /* * Catch all bounds-checking errors. @@ -128,7 +137,7 @@ * go all the way to the top level and get reported immediately. */ #define CATCH_BOUNDS_AND_DISSECTOR_ERRORS \ - CATCH4(BoundsError, ReportedBoundsError, ScsiBoundsError, DissectorError) + CATCH5(BoundsError, ReportedBoundsError, ScsiBoundsError, DissectorError, ReassemblyError) /* Usage: * @@ -287,6 +296,16 @@ (except_state|=EXCEPT_CAUGHT)) \ /* user's code goes here */ +#define CATCH5(v,w,x,y,z) \ + if (except_state == 0 && exc != 0 && \ + (exc->except_id.except_code == (v) || \ + exc->except_id.except_code == (w) || \ + exc->except_id.except_code == (x) || \ + exc->except_id.except_code == (y) || \ + exc->except_id.except_code == (z)) && \ + (except_state|=EXCEPT_CAUGHT)) \ + /* user's code goes here */ + #define CATCH_ALL \ if (except_state == 0 && exc != 0 && \ (except_state|=EXCEPT_CAUGHT)) \ @@ -307,6 +326,11 @@ #define THROW_MESSAGE(x, y) \ except_throw(XCEPT_GROUP_WIRESHARK, (x), (y)) +#define THROW_MESSAGE_ON(cond, x, y) G_STMT_START { \ + if ((cond)) \ + except_throw(XCEPT_GROUP_WIRESHARK, (x), (y)); \ +} G_STMT_END + #define GET_MESSAGE except_message(exc) #define RETHROW \ diff --git a/epan/reassemble.c b/epan/reassemble.c index 54e20b10db..3fb07a6d9d 100644 --- a/epan/reassemble.c +++ b/epan/reassemble.c @@ -544,7 +544,7 @@ fragment_set_tot_len(const packet_info *pinfo, const guint32 id, GHashTable *fra while (fd) { if (fd->offset > max_offset) { max_offset = fd->offset; - DISSECTOR_ASSERT(max_offset <= tot_len); + THROW_MESSAGE_ON(max_offset > tot_len, ReassemblyError, "Bad total reassembly block count"); } fd = fd->next; } @@ -553,14 +553,14 @@ fragment_set_tot_len(const packet_info *pinfo, const guint32 id, GHashTable *fra while (fd) { if (fd->offset + fd->len > max_offset) { max_offset = fd->offset + fd->len; - DISSECTOR_ASSERT(max_offset <= tot_len); + THROW_MESSAGE_ON(max_offset > tot_len, ReassemblyError, "Bad total reassembly length"); } fd = fd->next; } } if (fd_head->flags & FD_DEFRAGMENTED) { - DISSECTOR_ASSERT(max_offset == tot_len); + THROW_MESSAGE_ON(max_offset != tot_len, ReassemblyError, "Defragmented complete but total length not satisfied"); } /* We got this far so the value is sane. */ @@ -715,37 +715,52 @@ fragment_add_work(fragment_data *fd_head, tvbuff_t *tvb, const int offset, fragment_data *fd_i; guint32 max, dfpos; unsigned char *old_data; + const char *error = NULL; /* create new fd describing this fragment */ fd = g_slice_new(fragment_data); fd->next = NULL; fd->flags = 0; fd->frame = pinfo->fd->num; - if (fd->frame > fd_head->frame) - fd_head->frame = fd->frame; fd->offset = frag_offset; fd->len = frag_data_len; fd->data = NULL; - /* - * If it was already defragmented and this new fragment goes beyond - * data limits, set flag in already empty fds & point old fds to malloc'ed data. - */ - if(fd_head->flags & FD_DEFRAGMENTED && (frag_offset+frag_data_len) >= fd_head->datalen && - fd_head->flags & FD_PARTIAL_REASSEMBLY){ - for(fd_i=fd_head->next; fd_i; fd_i=fd_i->next){ - if( !fd_i->data ) { - fd_i->data = fd_head->data + fd_i->offset; - fd_i->flags |= FD_NOT_MALLOCED; + /* If it was already defragmented and this new fragment goes beyond the + * old data limits... */ + if(fd_head->flags & FD_DEFRAGMENTED && (frag_offset+frag_data_len) >= fd_head->datalen) { + /* If we've been requested to continue reassembly, set flag in + * already empty fds & point old fds to malloc'ed data. */ + if (fd_head->flags & FD_PARTIAL_REASSEMBLY) { + for(fd_i=fd_head->next; fd_i; fd_i=fd_i->next){ + if( !fd_i->data ) { + fd_i->data = fd_head->data + fd_i->offset; + fd_i->flags |= FD_NOT_MALLOCED; + } + fd_i->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS); } - fd_i->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS); + fd_head->flags &= ~(FD_DEFRAGMENTED|FD_PARTIAL_REASSEMBLY|FD_DATALEN_SET); + fd_head->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS); + fd_head->datalen=0; + fd_head->reassembled_in=0; + } + else { + /* Otherwise, bail out since we have no idea what to do + * with this fragment (and if we keep going we'll run + * past the end of a buffer sooner or later). + * + * XXX: Is ReportedBoundsError the right thing to throw? + */ + g_slice_free(fragment_data, fd); + THROW(ReportedBoundsError); } - fd_head->flags &= ~(FD_DEFRAGMENTED|FD_PARTIAL_REASSEMBLY|FD_DATALEN_SET); - fd_head->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS); - fd_head->datalen=0; - fd_head->reassembled_in=0; } + /* Do this after we may have bailed out (above) so that we don't leave + * fd_head->frame in a bad state if we do */ + if (fd->frame > fd_head->frame) + fd_head->frame = fd->frame; + if (!more_frags) { /* * This is the tail fragment in the sequence. @@ -878,19 +893,13 @@ fragment_add_work(fragment_data *fd_head, tvbuff_t *tvb, const int offset, if ( fd_i->offset+fd_i->len > dfpos ) { if (fd_i->offset+fd_i->len > max) - g_warning("Reassemble error in frame %u: offset %u + len %u > max %u", - pinfo->fd->num, fd_i->offset, - fd_i->len, max); + error = "offset + len > max"; else if (dfpos < fd_i->offset) - g_warning("Reassemble error in frame %u: dfpos %u < offset %u", - pinfo->fd->num, dfpos, fd_i->offset); + error = "dfpos < offset"; else if (dfpos-fd_i->offset > fd_i->len) - g_warning("Reassemble error in frame %u: dfpos %u - offset %u > len %u", - pinfo->fd->num, dfpos, fd_i->offset, - fd_i->len); + error = "dfpos - offset > len"; else if (!fd_head->data) - g_warning("Reassemble error in frame %u: no data", - pinfo->fd->num); + error = "no data"; else { if (fd_i->offset < dfpos) { fd_i->flags |= FD_OVERLAP; @@ -908,10 +917,9 @@ fragment_add_work(fragment_data *fd_head, tvbuff_t *tvb, const int offset, fd_i->len-(dfpos-fd_i->offset)); } } else { - if (fd_i->offset + fd_i->len < fd_i->offset) /* Integer overflow? */ - g_warning("Reassemble error in frame %u: offset %u + len %u < offset", - pinfo->fd->num, fd_i->offset, - fd_i->len); + if (fd_i->offset + fd_i->len < fd_i->offset) { /* Integer overflow? */ + error = "offset + len < offset"; + } } if( fd_i->flags & FD_NOT_MALLOCED ) fd_i->flags &= ~FD_NOT_MALLOCED; @@ -929,6 +937,11 @@ fragment_add_work(fragment_data *fd_head, tvbuff_t *tvb, const int offset, fd_head->flags |= FD_DEFRAGMENTED; fd_head->reassembled_in=pinfo->fd->num; + /* we don't throw until here to avoid leaking old_data and others */ + if (error) { + THROW_MESSAGE(ReassemblyError, error); + } + return TRUE; } diff --git a/epan/show_exception.c b/epan/show_exception.c index 68e061eb67..ddc41bff85 100644 --- a/epan/show_exception.c +++ b/epan/show_exception.c @@ -109,6 +109,23 @@ show_exception(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, dissector_error_nomsg : exception_message); break; + case ReassemblyError: + col_append_fstr(pinfo->cinfo, COL_INFO, + "[Reassembly error, protocol %s: %s]", + pinfo->current_proto, + exception_message == NULL ? + dissector_error_nomsg : exception_message); + item = proto_tree_add_protocol_format(tree, proto_malformed, tvb, 0, 0, + "[Reassembly error, protocol %s: %s]", + pinfo->current_proto, + exception_message == NULL ? + dissector_error_nomsg : exception_message); + expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR, + "%s", + exception_message == NULL ? + dissector_error_nomsg : exception_message); + break; + default: /* XXX - we want to know, if an unknown exception passed until here, don't we? */ g_assert_not_reached(); |