diff mbox series

[03/10] tools/xenstore: Don't assume conn->in points to the LU request

Message ID 20210616144324.31652-4-julien@xen.org (mailing list archive)
State New, archived
Headers show
Series tools/xenstored: Bug fixes + Improve Live-Update | expand

Commit Message

Julien Grall June 16, 2021, 2:43 p.m. UTC
From: Julien Grall <jgrall@amazon.com>

call_delayed() is currently assuming that conn->in is NULL when
handling delayed request. However, the connection is not paused.
Therefore new request can be processed and conn->in may be non-NULL
if we have only received a partial request.

Furthermore, as we overwrite conn->in, the current partial request
will not be transferred. This will result to corrupt the connection.

Rather than updating conn->in, stash the LU request in lu_status and
let each callback for delayed request to update conn->in when
necessary.

To keep a sane interface, the code to write the "OK" response the
LU request is moved in xenstored_core.c.

Fixes: c5ca1404b4 ("tools/xenstore: add support for delaying execution of a xenstore request")
Fixes: ed6eebf17d ("tools/xenstore: dump the xenstore state for live update")
Signed-off-by: Julien Grall <jgrall@amazon.com>

----

This is fixing bugs from two separate commits. I couldn't figure out
how to split in two patches without breaking bisection.
---
 tools/xenstore/xenstored_control.c | 41 ++++++++++++++++++++++++++++--
 tools/xenstore/xenstored_control.h |  3 +++
 tools/xenstore/xenstored_core.c    | 17 +++----------
 3 files changed, 46 insertions(+), 15 deletions(-)

Comments

Luca Fancellu June 21, 2021, 8:55 a.m. UTC | #1
> On 16 Jun 2021, at 15:43, Julien Grall <julien@xen.org> wrote:
> 
> From: Julien Grall <jgrall@amazon.com>
> 
> call_delayed() is currently assuming that conn->in is NULL when
> handling delayed request. However, the connection is not paused.
> Therefore new request can be processed and conn->in may be non-NULL
> if we have only received a partial request.
> 
> Furthermore, as we overwrite conn->in, the current partial request
> will not be transferred. This will result to corrupt the connection.
> 
> Rather than updating conn->in, stash the LU request in lu_status and
> let each callback for delayed request to update conn->in when
> necessary.
> 
> To keep a sane interface, the code to write the "OK" response the
> LU request is moved in xenstored_core.c.
> 
> Fixes: c5ca1404b4 ("tools/xenstore: add support for delaying execution of a xenstore request")
> Fixes: ed6eebf17d ("tools/xenstore: dump the xenstore state for live update")
> Signed-off-by: Julien Grall <jgrall@amazon.com>
> 
> ----
> 
> This is fixing bugs from two separate commits. I couldn't figure out
> how to split in two patches without breaking bisection.
> ---
> tools/xenstore/xenstored_control.c | 41 ++++++++++++++++++++++++++++--
> tools/xenstore/xenstored_control.h |  3 +++
> tools/xenstore/xenstored_core.c    | 17 +++----------
> 3 files changed, 46 insertions(+), 15 deletions(-)
> 
> diff --git a/tools/xenstore/xenstored_control.c b/tools/xenstore/xenstored_control.c
> index d08a2b961432..7acc2d134f9f 100644
> --- a/tools/xenstore/xenstored_control.c
> +++ b/tools/xenstore/xenstored_control.c
> @@ -50,6 +50,9 @@ struct live_update {
> 	/* For verification the correct connection is acting. */
> 	struct connection *conn;
> 
> +	/* Pointer to the command used to request LU */
> +	struct buffered_data *in;
> +
> #ifdef __MINIOS__
> 	void *kernel;
> 	unsigned int kernel_size;
> @@ -100,6 +103,7 @@ static const char *lu_begin(struct connection *conn)
> 	if (!lu_status)
> 		return "Allocation failure.";
> 	lu_status->conn = conn;
> +	lu_status->in = conn->in;
> 	talloc_set_destructor(lu_status, lu_destroy);
> 
> 	return NULL;
> @@ -110,11 +114,34 @@ struct connection *lu_get_connection(void)
> 	return lu_status ? lu_status->conn : NULL;
> }
> 
> +unsigned int lu_write_response(FILE *fp)
> +{
> +	struct xsd_sockmsg msg;
> +
> +	assert(lu_status);
> +
> +	msg = lu_status->in->hdr.msg;
> +
> +	msg.len = sizeof("OK");
> +	if (fp && fwrite(&msg, sizeof(msg), 1, fp) != 1)
> +		return 0;
> +	if (fp && fwrite("OK", msg.len, 1, fp) != 1)
> +		return 0;
> +
> +	return sizeof(msg) + msg.len;
> +}
> +
> #else
> struct connection *lu_get_connection(void)
> {
> 	return NULL;
> }
> +
> +unsigned int lu_write_response(FILE *fp)
> +{
> +	/* Unsupported */
> +	return 0;
> +}
> #endif
> 
> struct cmd_s {
> @@ -658,6 +685,8 @@ static bool do_lu_start(struct delayed_request *req)
> {
> 	time_t now = time(NULL);
> 	const char *ret;
> +	struct buffered_data *saved_in;
> +	struct connection *conn = lu_status->conn;
> 
> 	if (!lu_check_lu_allowed()) {
> 		if (now < lu_status->started_at + lu_status->timeout)
> @@ -668,8 +697,9 @@ static bool do_lu_start(struct delayed_request *req)
> 		}
> 	}
> 
> +	assert(req->in == lu_status->in);
> 	/* Dump out internal state, including "OK" for live update. */
> -	ret = lu_dump_state(req->in, lu_status->conn);
> +	ret = lu_dump_state(req->in, conn);
> 	if (!ret) {
> 		/* Perform the activation of new binary. */
> 		ret = lu_activate_binary(req->in);
> @@ -677,7 +707,14 @@ static bool do_lu_start(struct delayed_request *req)
> 
> 	/* We will reach this point only in case of failure. */
>  out:
> -	send_reply(lu_status->conn, XS_CONTROL, ret, strlen(ret) + 1);
> +	/*
> +	 * send_reply() will send the response for conn->in. Save the current
> +	 * conn->in and restore it afterwards.
> +	 */
> +	saved_in = conn->in;
> +	conn->in = req->in;
> +	send_reply(conn, XS_CONTROL, ret, strlen(ret) + 1);
> +	conn->in = saved_in;
> 	talloc_free(lu_status);
> 
> 	return true;
> diff --git a/tools/xenstore/xenstored_control.h b/tools/xenstore/xenstored_control.h
> index 6842b8d88760..27d7f19e4b7f 100644
> --- a/tools/xenstore/xenstored_control.h
> +++ b/tools/xenstore/xenstored_control.h
> @@ -20,3 +20,6 @@ int do_control(struct connection *conn, struct buffered_data *in);
> void lu_read_state(void);
> 
> struct connection *lu_get_connection(void);
> +
> +/* Write the "OK" response for the live-update command */
> +unsigned int lu_write_response(FILE *fp);
> diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c
> index 607187361d84..41b26d7094c8 100644
> --- a/tools/xenstore/xenstored_core.c
> +++ b/tools/xenstore/xenstored_core.c
> @@ -272,15 +272,10 @@ static int undelay_request(void *_req)
> 
> static void call_delayed(struct connection *conn, struct delayed_request *req)

Here the conn parameter is not needed anymore, or am I missing something?

Cheers,
Luca

> {
> -	assert(conn->in == NULL);
> -	conn->in = req->in;
> -
> 	if (req->func(req)) {
> 		undelay_request(req);
> 		talloc_set_destructor(req, NULL);
> 	}
> -
> -	conn->in = NULL;
> }
> 
> int delay_request(struct connection *conn, struct buffered_data *in,
> @@ -2375,7 +2370,7 @@ const char *dump_state_buffered_data(FILE *fp, const struct connection *c,
> 	struct buffered_data *out, *in = c->in;
> 	bool partial = true;
> 
> -	if (in && c != lu_get_connection()) {
> +	if (in) {
> 		len = in->inhdr ? in->used : sizeof(in->hdr);
> 		if (fp && fwrite(&in->hdr, len, 1, fp) != 1)
> 			return "Dump read data error";
> @@ -2416,16 +2411,12 @@ const char *dump_state_buffered_data(FILE *fp, const struct connection *c,
> 
> 	/* Add "OK" for live-update command. */
> 	if (c == lu_get_connection()) {
> -		struct xsd_sockmsg msg = c->in->hdr.msg;
> +		unsigned int rc = lu_write_response(fp);
> 
> -		msg.len = sizeof("OK");
> -		if (fp && fwrite(&msg, sizeof(msg), 1, fp) != 1)
> +		if (!rc)
> 			return "Dump buffered data error";
> -		len += sizeof(msg);
> -		if (fp && fwrite("OK", msg.len, 1, fp) != 1)
> 
> -			return "Dump buffered data error";
> -		len += msg.len;
> +		len += rc;
> 	}
> 
> 	if (sc)
> -- 
> 2.17.1
> 
>
Jürgen Groß June 24, 2021, 7:32 a.m. UTC | #2
On 16.06.21 16:43, Julien Grall wrote:
> From: Julien Grall <jgrall@amazon.com>
> 
> call_delayed() is currently assuming that conn->in is NULL when
> handling delayed request. However, the connection is not paused.
> Therefore new request can be processed and conn->in may be non-NULL
> if we have only received a partial request.
> 
> Furthermore, as we overwrite conn->in, the current partial request
> will not be transferred. This will result to corrupt the connection.
> 
> Rather than updating conn->in, stash the LU request in lu_status and
> let each callback for delayed request to update conn->in when
> necessary.
> 
> To keep a sane interface, the code to write the "OK" response the
> LU request is moved in xenstored_core.c.
> 
> Fixes: c5ca1404b4 ("tools/xenstore: add support for delaying execution of a xenstore request")
> Fixes: ed6eebf17d ("tools/xenstore: dump the xenstore state for live update")
> Signed-off-by: Julien Grall <jgrall@amazon.com>

With dropping the conn parameter from call_delayed as already
mentioned by Luca you can add my:

Reviewed-by: Juergen Gross <jgross@suse.com>


Juergen
Jürgen Groß June 24, 2021, 7:34 a.m. UTC | #3
On 24.06.21 09:32, Juergen Gross wrote:
> On 16.06.21 16:43, Julien Grall wrote:
>> From: Julien Grall <jgrall@amazon.com>
>>
>> call_delayed() is currently assuming that conn->in is NULL when
>> handling delayed request. However, the connection is not paused.
>> Therefore new request can be processed and conn->in may be non-NULL
>> if we have only received a partial request.
>>
>> Furthermore, as we overwrite conn->in, the current partial request
>> will not be transferred. This will result to corrupt the connection.
>>
>> Rather than updating conn->in, stash the LU request in lu_status and
>> let each callback for delayed request to update conn->in when
>> necessary.
>>
>> To keep a sane interface, the code to write the "OK" response the
>> LU request is moved in xenstored_core.c.
>>
>> Fixes: c5ca1404b4 ("tools/xenstore: add support for delaying execution 

>> of a xenstore request")
>> Fixes: ed6eebf17d ("tools/xenstore: dump the xenstore state for live 
>> update")
>> Signed-off-by: Julien Grall <jgrall@amazon.com>
> 
> With dropping the conn parameter from call_delayed as already
> mentioned by Luca you can add my:

Oh, please drop my request to delete the conn parameter, as it is being
used in patch 4 again.

> 
> Reviewed-by: Juergen Gross <jgross@suse.com>

This stands, of course.


Juergen
Luca Fancellu June 24, 2021, 7:56 a.m. UTC | #4
> On 24 Jun 2021, at 08:34, Juergen Gross <jgross@suse.com> wrote:
> 
> On 24.06.21 09:32, Juergen Gross wrote:
>> On 16.06.21 16:43, Julien Grall wrote:
>>> From: Julien Grall <jgrall@amazon.com>
>>> 
>>> call_delayed() is currently assuming that conn->in is NULL when
>>> handling delayed request. However, the connection is not paused.
>>> Therefore new request can be processed and conn->in may be non-NULL
>>> if we have only received a partial request.
>>> 
>>> Furthermore, as we overwrite conn->in, the current partial request
>>> will not be transferred. This will result to corrupt the connection.
>>> 
>>> Rather than updating conn->in, stash the LU request in lu_status and
>>> let each callback for delayed request to update conn->in when
>>> necessary.
>>> 
>>> To keep a sane interface, the code to write the "OK" response the
>>> LU request is moved in xenstored_core.c.
>>> 
>>> Fixes: c5ca1404b4 ("tools/xenstore: add support for delaying execution 
> 
>>> of a xenstore request")
>>> Fixes: ed6eebf17d ("tools/xenstore: dump the xenstore state for live update")
>>> Signed-off-by: Julien Grall <jgrall@amazon.com>
>> With dropping the conn parameter from call_delayed as already
>> mentioned by Luca you can add my:
> 
> Oh, please drop my request to delete the conn parameter, as it is being
> used in patch 4 again.
> 
>> Reviewed-by: Juergen Gross <jgross@suse.com>
> 
> This stands, of course.

Hi Juergen,

I’m sorry but I don’t see when the parameter is used, in patch 4 we have this call:

line 2344:
        if (delayed_requests) {
            list_for_each_entry(conn, &connections, list) {
                struct delayed_request *req, *tmp;

                list_for_each_entry_safe(req, tmp,
                             &conn->delayed, list)
                    call_delayed(conn, req);
            }
        }

But call_delayed is still this one:

Line 273:
static void call_delayed(struct connection *conn, struct delayed_request *req)
{
    if (req->func(req)) {
        undelay_request(req);
        talloc_set_destructor(req, NULL);
    }
}

Am I missing something?

Cheers,
Luca

> 
> 
> Juergen
> <OpenPGP_0xB0DE9DD628BF132F.asc>
Jürgen Groß June 24, 2021, 8:05 a.m. UTC | #5
On 24.06.21 09:56, Luca Fancellu wrote:
> 
> 
>> On 24 Jun 2021, at 08:34, Juergen Gross <jgross@suse.com> wrote:
>>
>> On 24.06.21 09:32, Juergen Gross wrote:
>>> On 16.06.21 16:43, Julien Grall wrote:
>>>> From: Julien Grall <jgrall@amazon.com>
>>>>
>>>> call_delayed() is currently assuming that conn->in is NULL when
>>>> handling delayed request. However, the connection is not paused.
>>>> Therefore new request can be processed and conn->in may be non-NULL
>>>> if we have only received a partial request.
>>>>
>>>> Furthermore, as we overwrite conn->in, the current partial request
>>>> will not be transferred. This will result to corrupt the connection.
>>>>
>>>> Rather than updating conn->in, stash the LU request in lu_status and
>>>> let each callback for delayed request to update conn->in when
>>>> necessary.
>>>>
>>>> To keep a sane interface, the code to write the "OK" response the
>>>> LU request is moved in xenstored_core.c.
>>>>
>>>> Fixes: c5ca1404b4 ("tools/xenstore: add support for delaying execution
>>
>>>> of a xenstore request")
>>>> Fixes: ed6eebf17d ("tools/xenstore: dump the xenstore state for live 
update")
>>>> Signed-off-by: Julien Grall <jgrall@amazon.com>
>>> With dropping the conn parameter from call_delayed as already
>>> mentioned by Luca you can add my:
>>
>> Oh, please drop my request to delete the conn parameter, as it is being
>> used in patch 4 again.
>>
>>> Reviewed-by: Juergen Gross <jgross@suse.com>
>>
>> This stands, of course.
> 
> Hi Juergen,
> 
> I’m sorry but I don’t see when the parameter is used, in patch 4 we have this call:
> 
> line 2344:
>          if (delayed_requests) {
>              list_for_each_entry(conn, &connections, list) {
>                  struct delayed_request *req, *tmp;
> 
>                  list_for_each_entry_safe(req, tmp,
>                               &conn->delayed, list)
>                      call_delayed(conn, req);
>              }
>          }
> 
> But call_delayed is still this one:
> 
> Line 273:
> static void call_delayed(struct connection *conn, struct delayed_request *req)
> {
>      if (req->func(req)) {
>          undelay_request(req);
>          talloc_set_destructor(req, NULL);
>      }
> }
> 
> Am I missing something?

Hmm, I seem to have mixed up something.

Yes, you are right. So off with the conn parameter (again).


Juergen
Julien Grall June 24, 2021, 8:06 a.m. UTC | #6
Hi Luca,

On 21/06/2021 10:55, Luca Fancellu wrote:
>> diff --git a/tools/xenstore/xenstored_control.h b/tools/xenstore/xenstored_control.h
>> index 6842b8d88760..27d7f19e4b7f 100644
>> --- a/tools/xenstore/xenstored_control.h
>> +++ b/tools/xenstore/xenstored_control.h
>> @@ -20,3 +20,6 @@ int do_control(struct connection *conn, struct buffered_data *in);
>> void lu_read_state(void);
>>
>> struct connection *lu_get_connection(void);
>> +
>> +/* Write the "OK" response for the live-update command */
>> +unsigned int lu_write_response(FILE *fp);
>> diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c
>> index 607187361d84..41b26d7094c8 100644
>> --- a/tools/xenstore/xenstored_core.c
>> +++ b/tools/xenstore/xenstored_core.c
>> @@ -272,15 +272,10 @@ static int undelay_request(void *_req)
>>
>> static void call_delayed(struct connection *conn, struct delayed_request *req)
> 
> Here the conn parameter is not needed anymore, or am I missing something?

The parameter is now unused. I will drop it.

Cheers,
diff mbox series

Patch

diff --git a/tools/xenstore/xenstored_control.c b/tools/xenstore/xenstored_control.c
index d08a2b961432..7acc2d134f9f 100644
--- a/tools/xenstore/xenstored_control.c
+++ b/tools/xenstore/xenstored_control.c
@@ -50,6 +50,9 @@  struct live_update {
 	/* For verification the correct connection is acting. */
 	struct connection *conn;
 
+	/* Pointer to the command used to request LU */
+	struct buffered_data *in;
+
 #ifdef __MINIOS__
 	void *kernel;
 	unsigned int kernel_size;
@@ -100,6 +103,7 @@  static const char *lu_begin(struct connection *conn)
 	if (!lu_status)
 		return "Allocation failure.";
 	lu_status->conn = conn;
+	lu_status->in = conn->in;
 	talloc_set_destructor(lu_status, lu_destroy);
 
 	return NULL;
@@ -110,11 +114,34 @@  struct connection *lu_get_connection(void)
 	return lu_status ? lu_status->conn : NULL;
 }
 
+unsigned int lu_write_response(FILE *fp)
+{
+	struct xsd_sockmsg msg;
+
+	assert(lu_status);
+
+	msg = lu_status->in->hdr.msg;
+
+	msg.len = sizeof("OK");
+	if (fp && fwrite(&msg, sizeof(msg), 1, fp) != 1)
+		return 0;
+	if (fp && fwrite("OK", msg.len, 1, fp) != 1)
+		return 0;
+
+	return sizeof(msg) + msg.len;
+}
+
 #else
 struct connection *lu_get_connection(void)
 {
 	return NULL;
 }
+
+unsigned int lu_write_response(FILE *fp)
+{
+	/* Unsupported */
+	return 0;
+}
 #endif
 
 struct cmd_s {
@@ -658,6 +685,8 @@  static bool do_lu_start(struct delayed_request *req)
 {
 	time_t now = time(NULL);
 	const char *ret;
+	struct buffered_data *saved_in;
+	struct connection *conn = lu_status->conn;
 
 	if (!lu_check_lu_allowed()) {
 		if (now < lu_status->started_at + lu_status->timeout)
@@ -668,8 +697,9 @@  static bool do_lu_start(struct delayed_request *req)
 		}
 	}
 
+	assert(req->in == lu_status->in);
 	/* Dump out internal state, including "OK" for live update. */
-	ret = lu_dump_state(req->in, lu_status->conn);
+	ret = lu_dump_state(req->in, conn);
 	if (!ret) {
 		/* Perform the activation of new binary. */
 		ret = lu_activate_binary(req->in);
@@ -677,7 +707,14 @@  static bool do_lu_start(struct delayed_request *req)
 
 	/* We will reach this point only in case of failure. */
  out:
-	send_reply(lu_status->conn, XS_CONTROL, ret, strlen(ret) + 1);
+	/*
+	 * send_reply() will send the response for conn->in. Save the current
+	 * conn->in and restore it afterwards.
+	 */
+	saved_in = conn->in;
+	conn->in = req->in;
+	send_reply(conn, XS_CONTROL, ret, strlen(ret) + 1);
+	conn->in = saved_in;
 	talloc_free(lu_status);
 
 	return true;
diff --git a/tools/xenstore/xenstored_control.h b/tools/xenstore/xenstored_control.h
index 6842b8d88760..27d7f19e4b7f 100644
--- a/tools/xenstore/xenstored_control.h
+++ b/tools/xenstore/xenstored_control.h
@@ -20,3 +20,6 @@  int do_control(struct connection *conn, struct buffered_data *in);
 void lu_read_state(void);
 
 struct connection *lu_get_connection(void);
+
+/* Write the "OK" response for the live-update command */
+unsigned int lu_write_response(FILE *fp);
diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c
index 607187361d84..41b26d7094c8 100644
--- a/tools/xenstore/xenstored_core.c
+++ b/tools/xenstore/xenstored_core.c
@@ -272,15 +272,10 @@  static int undelay_request(void *_req)
 
 static void call_delayed(struct connection *conn, struct delayed_request *req)
 {
-	assert(conn->in == NULL);
-	conn->in = req->in;
-
 	if (req->func(req)) {
 		undelay_request(req);
 		talloc_set_destructor(req, NULL);
 	}
-
-	conn->in = NULL;
 }
 
 int delay_request(struct connection *conn, struct buffered_data *in,
@@ -2375,7 +2370,7 @@  const char *dump_state_buffered_data(FILE *fp, const struct connection *c,
 	struct buffered_data *out, *in = c->in;
 	bool partial = true;
 
-	if (in && c != lu_get_connection()) {
+	if (in) {
 		len = in->inhdr ? in->used : sizeof(in->hdr);
 		if (fp && fwrite(&in->hdr, len, 1, fp) != 1)
 			return "Dump read data error";
@@ -2416,16 +2411,12 @@  const char *dump_state_buffered_data(FILE *fp, const struct connection *c,
 
 	/* Add "OK" for live-update command. */
 	if (c == lu_get_connection()) {
-		struct xsd_sockmsg msg = c->in->hdr.msg;
+		unsigned int rc = lu_write_response(fp);
 
-		msg.len = sizeof("OK");
-		if (fp && fwrite(&msg, sizeof(msg), 1, fp) != 1)
+		if (!rc)
 			return "Dump buffered data error";
-		len += sizeof(msg);
-		if (fp && fwrite("OK", msg.len, 1, fp) != 1)
 
-			return "Dump buffered data error";
-		len += msg.len;
+		len += rc;
 	}
 
 	if (sc)