[Python-checkins] CVS: python/dist/src/Objects dictobject.c,2.83,2.84

Tim Peters tim_one@users.sourceforge.net
Thu, 10 May 2001 01:32:46 -0700


Update of /cvsroot/python/python/dist/src/Objects
In directory usw-pr-cvs1:/tmp/cvs-serv28217/python/dist/src/Objects

Modified Files:
	dictobject.c 
Log Message:
SF bug #422121 Insecurities in dict comparison.
Fixed a half dozen ways in which general dict comparison could crash
Python (even cause Win98SE to reboot) in the presence of kay and/or
value comparison routines that mutate the dict during dict comparison.
Bugfix candidate.


Index: dictobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/dictobject.c,v
retrieving revision 2.83
retrieving revision 2.84
diff -C2 -r2.83 -r2.84
*** dictobject.c	2001/05/08 04:38:29	2.83
--- dictobject.c	2001/05/10 08:32:44	2.84
***************
*** 982,1022 ****
  /* Subroutine which returns the smallest key in a for which b's value
     is different or absent.  The value is returned too, through the
!    pval argument.  No reference counts are incremented. */
  
  static PyObject *
  characterize(dictobject *a, dictobject *b, PyObject **pval)
  {
! 	PyObject *diff = NULL;
  	int i, cmp;
  
- 	*pval = NULL;
  	for (i = 0; i < a->ma_size; i++) {
! 		if (a->ma_table[i].me_value != NULL) {
! 			PyObject *key = a->ma_table[i].me_key;
! 			PyObject *aval, *bval;
! 			if (diff != NULL) {
! 			    cmp = PyObject_RichCompareBool(diff, key, Py_LT);
! 			    if (cmp < 0)
! 				return NULL;
! 			    if (cmp > 0)
! 				continue;
  			}
! 			aval = a->ma_table[i].me_value;
! 			bval = PyDict_GetItem((PyObject *)b, key);
! 			if (bval == NULL)
! 				cmp = 0;
! 			else {
! 			    cmp = PyObject_RichCompareBool(aval, bval, Py_EQ);
! 			    if (cmp < 0)
! 				return NULL;
! 			}
! 			if (cmp == 0)
  			{
! 				diff = key;
! 				*pval = aval;
  			}
  		}
  	}
! 	return diff;
  }
  
--- 982,1062 ----
  /* Subroutine which returns the smallest key in a for which b's value
     is different or absent.  The value is returned too, through the
!    pval argument.  Both are NULL if no key in a is found for which b's status
!    differs.  The refcounts on (and only on) non-NULL *pval and function return
!    values must be decremented by the caller (characterize() increments them
!    to ensure that mutating comparison and PyDict_GetItem calls can't delete
!    them before the caller is done looking at them). */
  
  static PyObject *
  characterize(dictobject *a, dictobject *b, PyObject **pval)
  {
! 	PyObject *akey = NULL; /* smallest key in a s.t. a[akey] != b[akey] */
! 	PyObject *aval = NULL; /* a[akey] */
  	int i, cmp;
  
  	for (i = 0; i < a->ma_size; i++) {
! 		PyObject *thiskey, *thisaval, *thisbval;
! 		if (a->ma_table[i].me_value == NULL)
! 			continue;
! 		thiskey = a->ma_table[i].me_key;
! 		Py_INCREF(thiskey);  /* keep alive across compares */
! 		if (akey != NULL) {
! 			cmp = PyObject_RichCompareBool(akey, thiskey, Py_LT);
! 			if (cmp < 0) {
! 				Py_DECREF(thiskey);
! 				goto Fail;
  			}
! 			if (cmp > 0 ||
! 			    i >= a->ma_size ||
! 			    a->ma_table[i].me_value == NULL)
  			{
! 				/* Not the *smallest* a key; or maybe it is
! 				 * but the compare shrunk the dict so we can't
! 				 * find its associated value anymore; or
! 				 * maybe it is but the compare deleted the
! 				 * a[thiskey] entry.
! 				 */
! 				Py_DECREF(thiskey);
! 				continue;
  			}
  		}
+ 
+ 		/* Compare a[thiskey] to b[thiskey]; cmp <- true iff equal. */
+ 		thisaval = a->ma_table[i].me_value;
+ 		assert(thisaval);
+ 		Py_INCREF(thisaval);   /* keep alive */
+ 		thisbval = PyDict_GetItem((PyObject *)b, thiskey);
+ 		if (thisbval == NULL)
+ 			cmp = 0;
+ 		else {
+ 			/* both dicts have thiskey:  same values? */
+ 			cmp = PyObject_RichCompareBool(
+ 						thisaval, thisbval, Py_EQ);
+ 			if (cmp < 0) {
+ 		    		Py_DECREF(thiskey);
+ 		    		Py_DECREF(thisaval);
+ 		    		goto Fail;
+ 			}
+ 		}
+ 		if (cmp == 0) {
+ 			/* New winner. */
+ 			Py_XDECREF(akey);
+ 			Py_XDECREF(aval);
+ 			akey = thiskey;
+ 			aval = thisaval;
+ 		}
+ 		else {
+ 			Py_DECREF(thiskey);
+ 			Py_DECREF(thisaval);
+ 		}
  	}
! 	*pval = aval;
! 	return akey;
! 
! Fail:
! 	Py_XDECREF(akey);
! 	Py_XDECREF(aval);
! 	*pval = NULL;
! 	return NULL;
  }
  
***************
*** 1032,1048 ****
  	else if (a->ma_used > b->ma_used)
  		return 1;	/* b is shorter */
  	/* Same length -- check all keys */
  	adiff = characterize(a, b, &aval);
! 	if (adiff == NULL && PyErr_Occurred())
! 		return -1;
! 	if (adiff == NULL)
! 		return 0;	/* a is a subset with the same length */
  	bdiff = characterize(b, a, &bval);
! 	if (bdiff == NULL && PyErr_Occurred())
! 		return -1;
! 	/* bdiff == NULL would be impossible now */
! 	res = PyObject_Compare(adiff, bdiff);
! 	if (res == 0)
  		res = PyObject_Compare(aval, bval);
  	return res;
  }
--- 1072,1109 ----
  	else if (a->ma_used > b->ma_used)
  		return 1;	/* b is shorter */
+ 
  	/* Same length -- check all keys */
+ 	bdiff = bval = NULL;
  	adiff = characterize(a, b, &aval);
! 	if (adiff == NULL) {
! 		assert(!aval);
! 		/* Either an error, or a is a subst with the same length so
! 		 * must be equal.
! 		 */
! 		res = PyErr_Occurred() ? -1 : 0;
! 		goto Finished;
! 	}
  	bdiff = characterize(b, a, &bval);
! 	if (bdiff == NULL && PyErr_Occurred()) {
! 		assert(!bval);
! 		res = -1;
! 		goto Finished;
! 	}
! 	res = 0;
! 	if (bdiff) {
! 		/* bdiff == NULL "should be" impossible now, but perhaps
! 		 * the last comparison done by the characterize() on a had
! 		 * the side effect of making the dicts equal!
! 		 */
! 		res = PyObject_Compare(adiff, bdiff);
! 	}
! 	if (res == 0 && bval != NULL)
  		res = PyObject_Compare(aval, bval);
+ 
+ Finished:
+ 	Py_XDECREF(adiff);
+ 	Py_XDECREF(bdiff);
+ 	Py_XDECREF(aval);
+ 	Py_XDECREF(bval);
  	return res;
  }