AUDIT: Safe usage of before48/after48 in the DCCP code

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



We were having a bit of discussion on netdev@vger regarding the new definition of before48
which was motivated at the end of the email http://www.mail-archive.com/dccp@xxxxxxxxxxxxxxx/msg01153.html

The result of the netdev discussion was that one can not argue that before48/after48 is safer
or less ambiguous to use unless one performs an audit against all occurrences in the code.

Therefore such a due analysis is provided below.


		1) General cases
		================

CASE 1: calls of the form 
	 * if (before48(a,b)) or 
	 * if (after48(b,a))
        are not ambiguous due to the definition of before48 (see above link for details).

CASE 2: calls of the form
	 * if (!before48(a, b)) or
	 * if (!after48(b, a))
        can be ambiguous - it depends on the context if the ambiguity requires further
        resolution. 


		2) Detailed breakdown of before48/after48 usage
		===============================================

Here is the complete list of cases where before48/after48 are currently used in the code, 
plus analysis whether the use of the new definition of before48 introduces problems or not.

	
net/dccp/dccp.h-136-static inline u64 max48(const u64 seq1, const u64 seq2)
net/dccp/dccp.h-137-{
net/dccp/dccp.h:138:	return after48(seq1, seq2) ? seq1 : seq2;
net/dccp/dccp.h-139-}
net/dccp/dccp.h-140-
==> CASE 1 - safe.
		

net/dccp/ccids/ccid2.c-586-		/* it's a later packet */
net/dccp/ccids/ccid2.c:587:		else if (after48(seqno, hctx->ccid2hctx_rpseq)) {
net/dccp/ccids/ccid2.c-588-			hctx->ccid2hctx_rpdupack++;
==> CASE 1 - safe.


net/dccp/ccids/ccid2.c-614-	ackno = DCCP_SKB_CB(skb)->dccpd_ack_seq;
net/dccp/ccids/ccid2.c:615:	if (after48(ackno, hctx->ccid2hctx_high_ack))
net/dccp/ccids/ccid2.c-616-		hctx->ccid2hctx_high_ack = ackno;
==> CASE 1 - safe.


net/dccp/ccids/ccid2.c:650:			while (after48(seqp->ccid2s_seq, ackno)) {
net/dccp/ccids/ccid2.c-651-				if (seqp == hctx->ccid2hctx_seqt) {
net/dccp/ccids/ccid2.c-652-					done = 1;
net/dccp/ccids/ccid2.c-653-					break;
==> CASE 1 - safe.


net/dccp/ccids/lib/packet_history.c-225-	if (!list_empty(li_list)) {
net/dccp/ccids/lib/packet_history.c-226-		list_for_each_entry_safe(entry, next, rx_list, dccphrx_node) {
net/dccp/ccids/lib/packet_history.c-227-			if (num_later == 0) {
net/dccp/ccids/lib/packet_history.c:228:				if (after48(nonloss_seqno,
net/dccp/ccids/lib/packet_history.c-229-				   entry->dccphrx_seqno)) {
net/dccp/ccids/lib/packet_history.c-230-					list_del_init(&entry->dccphrx_node);
net/dccp/ccids/lib/packet_history.c-231-					dccp_rx_hist_entry_delete(hist, entry);
==> CASE 1 - safe.


net/dccp/ackvec.c-268-	if (av->dccpav_vec_len == 0) {
net/dccp/ackvec.c-269-		av->dccpav_buf[av->dccpav_buf_head] = state;
net/dccp/ackvec.c-270-		av->dccpav_vec_len = 1;
net/dccp/ackvec.c:271:	} else if (after48(ackno, av->dccpav_buf_ackno)) {
net/dccp/ackvec.c-272-		const u64 delta = dccp_delta_seqno(av->dccpav_buf_ackno,
==> CASE 1 - safe.


net/dccp/ackvec.c-431-            /*
net/dccp/ackvec.c-432-             * If our AVR sequence number is greater than the ack, go
net/dccp/ackvec.c-433-             * forward in the AVR list until it is not so.
net/dccp/ackvec.c-434-             */
net/dccp/ackvec.c-435-		list_for_each_entry_from(avr, &av->dccpav_records,
net/dccp/ackvec.c-436-					 dccpavr_node) {
net/dccp/ackvec.c:437:			if (!after48(avr->dccpavr_ack_seqno, *ackno))
net/dccp/ackvec.c-438-				goto found;
net/dccp/ackvec.c-439-		}
net/dccp/ackvec.c-440-          /* End of the dccpav_records list, not found, exit */
net/dccp/ackvec.c-441-          break;

==> CASE 2: The code jumps to found whenever !before48(*ackno, avr->dccpavr_ack_seqno) or if the two arguments are
            2^47 apart. 
             * with the old definition the case "2^47 apart"  was considered as 'not found';
	     * with the new definition the case "2^47 apart"  is  considered as 'found'.
            However: Please correct me if I am wrong, but I think that the case "2^47 apart" is entirely irrelevant
            here. My argument to support this is that Ack Vectors refer to successive sequence numbers (modulo 
            wraparound, but we are considering this already). The highest possible difference of sequence numbers that
            fit into a single Ack Vector is 16192 (cf. RFC 4340, 11.4) - which is far less than 2^47. 
            Hence this use is safe.

net/dccp/ackvec.c:60:		BUG_ON(before48(avr->dccpavr_ack_seqno,
net/dccp/ackvec.c-61-                           head->dccpavr_ack_seqno));
==> CASE 1 - safe.

net/dccp/minisocks.c:199:		if (after48(DCCP_SKB_CB(skb)->dccpd_seq, dreq->dreq_isr)) {
net/dccp/minisocks.c-200-			dccp_pr_debug("Retransmitted REQUEST\n");
==> CASE 1 - safe.


net/dccp/ccids/ccid2.c:619:	while (before48(seqp->ccid2s_seq, ackno)) {
net/dccp/ccids/ccid2.c-620-		seqp = seqp->ccid2s_next;
==> CASE 1 - safe.


net/dccp/ccids/ccid2.c-701-	 * Check for NUMDUPACK
net/dccp/ccids/ccid2.c-702-	 */
net/dccp/ccids/ccid2.c-703-	seqp = hctx->ccid2hctx_seqt;
net/dccp/ccids/ccid2.c:704:	while (before48(seqp->ccid2s_seq, hctx->ccid2hctx_high_ack)) {
==> CASE 1 - safe.


net/dccp/input.c-74-      *   Step 5: Prepare sequence numbers for Sync
net/dccp/input.c-75-      *     If P.type == Sync or P.type == SyncAck,
net/dccp/input.c-76-      *        If S.AWL <= P.ackno <= S.AWH and P.seqno >= S.SWL,
net/dccp/input.c-77-      *           / * P is valid, so update sequence number variables
net/dccp/input.c-78-      *               accordingly.  After this update, P will pass the tests
net/dccp/input.c-79-      *               in Step 6.  A SyncAck is generated if necessary in
net/dccp/input.c-80-      *               Step 15 * /
net/dccp/input.c-81-      *           Update S.GSR, S.SWL, S.SWH
net/dccp/input.c-82-      *        Otherwise,
net/dccp/input.c-83-      *           Drop packet and return
net/dccp/input.c-84-      */
net/dccp/input.c-85-     if (dh->dccph_type == DCCP_PKT_SYNC ||
net/dccp/input.c-86-         dh->dccph_type == DCCP_PKT_SYNCACK) {
net/dccp/input.c-87-            if (between48(DCCP_SKB_CB(skb)->dccpd_ack_seq,
net/dccp/input.c-88-                           dp->dccps_awl, dp->dccps_awh) &&
net/dccp/input.c:89:		    !before48(DCCP_SKB_CB(skb)->dccpd_seq, dp->dccps_swl))
net/dccp/input.c-90-			dccp_update_gsr(sk, DCCP_SKB_CB(skb)->dccpd_seq);

==> CASE 2: Here swl corresponds to SWL as defined in RFC 4340, 7.5.1. 
             * with the old definition, gsr was not updated when P.seqno and S.SWL are 2^47 apart;
             * with the new definition, gsr is      updated when P.seqno and S.SWL are 2^47 apart.
          
==> TODO:  To handle this case correctly, we need to reconsider the intention: "P.seqno >= S.SWL". 
           This can without problem be captured by the dccp_delta_seqno(a, b) function, which returns 
           a value >= 0 when a==b or a is `before' b in the new definition. Hence, I'd suggest to replace 
                   !before48(DCCP_SKB_CB(skb)->dccpd_seq, dp->dccps_swl)                 with
                    dccp_delta_seqno(dp->dccps_swl, DCCP_SKB_CB(skb)->dccpd_seq) >= 0



net/dccp/input.c-187-    case DCCP_PKT_REQUEST:
net/dccp/input.c-188-            /* Step 7
net/dccp/input.c-189-             *   or (S.is_server and P.type == Response)
net/dccp/input.c-190-             *   or (S.is_client and P.type == Request)
net/dccp/input.c-191-             *   or (S.state >= OPEN and P.type == Request
net/dccp/input.c-192-             *      and P.seqno >= S.OSR)
net/dccp/input.c-193-             *    or (S.state >= OPEN and P.type == Response
net/dccp/input.c-194-             *      and P.seqno >= S.OSR)
net/dccp/input.c-195-             *    or (S.state == RESPOND and P.type == Data),
net/dccp/input.c-196-             *  Send Sync packet acknowledging P.seqno
net/dccp/input.c-197-             *  Drop packet and return
net/dccp/input.c-198-             */
net/dccp/input.c-199-            if (dp->dccps_role != DCCP_ROLE_LISTEN)
net/dccp/input.c-200-                    goto send_sync;
net/dccp/input.c-201-            goto check_seq;
net/dccp/input.c-202-    case DCCP_PKT_RESPONSE:
net/dccp/input.c-203-            if (dp->dccps_role != DCCP_ROLE_CLIENT)
net/dccp/input.c-204-                    goto send_sync;
net/dccp/input.c-205-check_seq:
net/dccp/input.c:206:		if (!before48(DCCP_SKB_CB(skb)->dccpd_seq, dp->dccps_osr)) {
net/dccp/input.c-207-send_sync:
net/dccp/input.c-208-			dccp_send_sync(sk, DCCP_SKB_CB(skb)->dccpd_seq,
net/dccp/input.c-209-				       DCCP_PKT_SYNC);

==> CASE 2: Here we have the same case in principle as above. A sync shall be sent if the condition
            "P.seqno >= S.OSR" is true. 
	      * with the old definition, a sync is not sent if P.seqno and S.OSR are 2^47 apart; 
              * with the new definition, a sync is     sent if P.seqno and S.OSR are 2^47 apart.
            Nothing harmful happens, the packet is dropped eventually anyway. It seems that whether
            using the old or new definition here is debatable; to make it consistent with the above case;
            one could consider replacing
                 !before48(DCCP_SKB_CB(skb)->dccpd_seq, dp->dccps_osr)                    with
                 dccp_delta_seqno(dp->dccps_osr, DCCP_SKB_CB(skb)->dccpd_seq) >=  0
            if desired.



		3) Conclusion
		=============
There are only two cases which need special consideration, all other uses of before48/after48 in the DCCP
code are unproblematic. I shall provide a patch to update the two cases.
-
To unsubscribe from this list: send the line "unsubscribe dccp" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel]     [IETF DCCP]     [Linux Networking]     [Git]     [Security]     [Linux Assembly]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]

  Powered by Linux