summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2013-12-09 20:27:32 +0000
committerFlorian Dold <florian.dold@gmail.com>2013-12-09 20:27:32 +0000
commite9a2778efa6e4ee9940cdb56face621dc319787f (patch)
treeac1cf075942d17286318b2fc7fbdc9a55297bb76
parentc712a1f8b2df7406ac6d7a3346e2b066a23b37ef (diff)
- fix use after free in set union (#3178)
-rw-r--r--src/set/gnunet-service-set.c17
-rw-r--r--src/set/gnunet-service-set.h8
-rw-r--r--src/set/gnunet-service-set_union.c5
-rw-r--r--src/set/test_set_union_result_full.c15
-rw-r--r--src/util/mq.c9
5 files changed, 44 insertions, 10 deletions
diff --git a/src/set/gnunet-service-set.c b/src/set/gnunet-service-set.c
index cf03b06aa..428a9d002 100644
--- a/src/set/gnunet-service-set.c
+++ b/src/set/gnunet-service-set.c
@@ -955,8 +955,8 @@ handle_client_evaluate (void *cls,
GNUNET_CONTAINER_DLL_insert (set->ops_head, set->ops_tail, op);
op->channel = GNUNET_MESH_channel_create (mesh, op, &msg->target_peer,
- GNUNET_APPLICATION_TYPE_SET,
- GNUNET_MESH_OPTION_RELIABLE);
+ GNUNET_APPLICATION_TYPE_SET,
+ GNUNET_MESH_OPTION_RELIABLE);
op->mq = GNUNET_MESH_mq_create (op->channel);
@@ -1261,17 +1261,28 @@ channel_new_cb (void *cls,
*/
static void
channel_end_cb (void *cls,
- const struct GNUNET_MESH_Channel *channel, void *channel_ctx)
+ const struct GNUNET_MESH_Channel *channel, void *channel_ctx)
{
struct Operation *op = channel_ctx;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"channel end cb called\n");
op->channel = NULL;
+ /* the vt can be null if a client already requested canceling op. */
if (NULL != op->vt)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "calling peer disconnect due to channel end\n");
op->vt->peer_disconnect (op);
+ }
+
+ if (GNUNET_YES == op->keep)
+ return;
+
/* mesh will never call us with the context again! */
GNUNET_free (channel_ctx);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "channel end cb finished\n");
}
diff --git a/src/set/gnunet-service-set.h b/src/set/gnunet-service-set.h
index d82c72b16..95a24119b 100644
--- a/src/set/gnunet-service-set.h
+++ b/src/set/gnunet-service-set.h
@@ -311,8 +311,6 @@ struct Operation
/**
* GNUNET_YES if this is not a "real" set operation yet, and we still
* need to wait for the other peer to give us more details.
- *
- * //TODO: replace with state-enum
*/
int is_incoming;
@@ -346,6 +344,12 @@ struct Operation
* a linked list.
*/
struct Operation *prev;
+
+ /**
+ * Set to GNUNET_YES if the set service should not free
+ * the operation, as it is still needed (e.g. in some scheduled task).
+ */
+ int keep;
};
diff --git a/src/set/gnunet-service-set_union.c b/src/set/gnunet-service-set_union.c
index be50ec8d6..6bb28471a 100644
--- a/src/set/gnunet-service-set_union.c
+++ b/src/set/gnunet-service-set_union.c
@@ -982,12 +982,15 @@ send_done_and_destroy (void *cls)
struct Operation *op = cls;
struct GNUNET_MQ_Envelope *ev;
struct GNUNET_SET_ResultMessage *rm;
+ int keep = op->keep;
ev = GNUNET_MQ_msg (rm, GNUNET_MESSAGE_TYPE_SET_RESULT);
rm->request_id = htonl (op->spec->client_request_id);
rm->result_status = htons (GNUNET_SET_STATUS_DONE);
rm->element_type = htons (0);
GNUNET_MQ_send (op->spec->set->client_mq, ev);
_GSS_operation_destroy (op);
+ if (GNUNET_YES == keep)
+ GNUNET_free (op);
}
@@ -1060,6 +1063,8 @@ finish_and_destroy (struct Operation *op)
if (GNUNET_SET_RESULT_FULL == op->spec->result_mode)
{
+ /* prevent that the op is free'd by the tunnel end handler */
+ op->keep = GNUNET_YES;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "sending full result set\n");
GNUNET_assert (NULL == op->state->full_result_iter);
op->state->full_result_iter =
diff --git a/src/set/test_set_union_result_full.c b/src/set/test_set_union_result_full.c
index e54332b8b..2ddd65c7c 100644
--- a/src/set/test_set_union_result_full.c
+++ b/src/set/test_set_union_result_full.c
@@ -28,6 +28,8 @@
#include "gnunet_set_service.h"
+static int ret;
+
static struct GNUNET_PeerIdentity local_id;
static struct GNUNET_HashCode app_id;
@@ -50,6 +52,7 @@ result_cb_set1 (void *cls, const struct GNUNET_SET_Element *element,
break;
case GNUNET_SET_STATUS_FAILURE:
printf ("set 1: failure\n");
+ ret = 1;
break;
case GNUNET_SET_STATUS_DONE:
printf ("set 1: done\n");
@@ -72,6 +75,7 @@ result_cb_set2 (void *cls, const struct GNUNET_SET_Element *element,
break;
case GNUNET_SET_STATUS_FAILURE:
printf ("set 2: failure\n");
+ ret = 1;
break;
case GNUNET_SET_STATUS_DONE:
printf ("set 2: done\n");
@@ -245,11 +249,12 @@ run (void *cls,
int
main (int argc, char **argv)
{
- int ret;
-
- ret = GNUNET_TESTING_peer_run ("test_set_api",
- "test_set.conf",
- &run, NULL);
+ if (0 != GNUNET_TESTING_peer_run ("test_set_api",
+ "test_set.conf",
+ &run, NULL))
+ {
+ return 0;
+ }
return ret;
}
diff --git a/src/util/mq.c b/src/util/mq.c
index bc13bbb36..a4691ff2c 100644
--- a/src/util/mq.c
+++ b/src/util/mq.c
@@ -268,6 +268,9 @@ impl_send_continue (void *cls,
struct GNUNET_MQ_Handle *mq = cls;
struct GNUNET_MQ_Envelope *current_envelope;
+ if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
+ return;
+
mq->continue_task = GNUNET_SCHEDULER_NO_TASK;
/* call is only valid if we're actually currently sending
* a message */
@@ -466,6 +469,12 @@ server_client_destroy_impl (struct GNUNET_MQ_Handle *mq,
{
struct ServerClientSocketState *state = impl_state;
+ if (NULL != state->th)
+ {
+ GNUNET_SERVER_notify_transmit_ready_cancel (state->th);
+ state->th = NULL;
+ }
+
GNUNET_assert (NULL != mq);
GNUNET_assert (NULL != state);
GNUNET_SERVER_client_drop (state->client);