Skip to contentSkip to conversation

Make qemu_peek_buffer loop until it gets it's data

PATCH Superseded Dr. David Alan Gilbert (git) 2 participants 6 messages Mar 20, 2014 – Mar 21, 2014

v1 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Make qemu_peek_buffer loop until it gets it's data

From: "Dr. David Alan Gilbert" <[email protected]>

Make qemu_peek_buffer repatedly call fill_buffer until it gets all the data it requires, or until there is an error.

At the moment, qemu_peek_buffer will try one qemu_fill_buffer if there isn't enough data waiting, however the kernel is entitled to return just a few bytes, and still leave qemu_peek_buffer with less bytes than it needed. I've seen this fail in a dev world, and I think it could theoretically fail in the peeking of the subsection headers in the current world.

Ditto for qemu_peek_byte (which can only be affected due to it's offset).

Simplify qemu_get_buffer since it can now rely on qemu_peek_buffer to loop.

Use size_t rather than int for size parameters, (and result for those functions that never return -errno).

Fail vmstate_subsection_load if idstr couldn't be peek'd

---
 include/migration/qemu-file.h |  6 ++--
 qemu-file.c                   | 64 +++++++++++++++++++++++++++----------------
 vmstate.c                     |  2 +-
 3 files changed, 44 insertions(+), 28 deletions(-)
include/migration/qemu-file.h +3 -3
index a191fb6..cc45c87 100644
@@ -121,11 +121,11 @@ static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v)
 void qemu_put_be16(QEMUFile *f, unsigned int v);
 void qemu_put_be32(QEMUFile *f, unsigned int v);
 void qemu_put_be64(QEMUFile *f, uint64_t v);
-int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset);
-int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size);
+size_t qemu_peek_buffer(QEMUFile *f, uint8_t *buf, size_t size, size_t offset);
+size_t qemu_get_buffer(QEMUFile *f, uint8_t *buf, size_t size);
 int qemu_peek_byte(QEMUFile *f, int offset);
 int qemu_get_byte(QEMUFile *f);
-void qemu_file_skip(QEMUFile *f, int size);
+void qemu_file_skip(QEMUFile *f, size_t size);
 void qemu_update_position(QEMUFile *f, size_t size);
 
 static inline unsigned int qemu_get_ubyte(QEMUFile *f)
qemu-file.c +40 -24

113 lines not on this page · raw message

vmstate.c +1 -1
index d1f5eb0..b8e6e31 100644
@@ -170,7 +170,7 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
         }
         size = qemu_peek_buffer(f, (uint8_t *)idstr, len, 2);
         if (size != len) {
-            return 0;
+            return -EIO;
         }
         idstr[size] = 0;
 
Signature · 1 line
1.8.5.3
#1 lore raw
Quoted 12 lines · Dr. David Alan Gilbert (git)

From: "Dr. David Alan Gilbert" <[email protected]>

Make qemu_peek_buffer repatedly call fill_buffer until it gets all the data it requires, or until there is an error.

At the moment, qemu_peek_buffer will try one qemu_fill_buffer if there isn't enough data waiting, however the kernel is entitled to return just a few bytes, and still leave qemu_peek_buffer with less bytes than it needed. I've seen this fail in a dev world, and I think it could theoretically fail in the peeking of the subsection headers in the current world.

Quoted 5 lines

Ditto for qemu_peek_byte (which can only be affected due to it's offset).

Simplify qemu_get_buffer since it can now rely on qemu_peek_buffer to loop.

I think this one is wrong, will explain there.

Quoted 2 lines

Use size_t rather than int for size parameters, (and result for those functions that never return -errno).

Nice.

Quoted 14 lines
qemu-file.c +3 -8
-int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size)
+size_t qemu_get_buffer(QEMUFile *f, uint8_t *buf, size_t size)
 {
-    int pending = size;
-    int done = 0;
+    size_t res;
 
-    while (pending > 0) {
-        int res;
+    res = qemu_peek_buffer(f, buf, size, 0);
 
-        res = qemu_peek_buffer(f, buf, pending, 0);
-        if (res == 0) {
-            return done;

qemu-file.c int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset) · line 743

Quoted 10 lines
qemu-file.c +3 -7
-        }
-        qemu_file_skip(f, res);
-        buf += res;
-        pending -= res;
-        done += res;
-    }
-    return done;
+    qemu_file_skip(f, res);
+
+    return res;

I think this is "theoretical" (a.k.a. no user of this functionality). As this was coded, we could receive buffers bigger than IOBUF_SIZE, with your change, we can't. Just maintating the loop should fix this, right?

qemu-file.c int qemu_peek_byte(QEMUFile *f, int offset) · line 755

Quoted 4 lines
qemu-file.c +4 -0
+    while (index >= f->buf_size) {
+        int received = qemu_fill_buffer(f);
+
+        if (received <= 0) {

here, I don't know really what to do. We just need one character, so the 1st call to qemu_fill_buffer() gives it to us, or we are already on problems. i.e. no need of the while() loop.

On the other hand, having exactly the same code looks so nice.

At some point I was thinking about making qemu_peek_byte() to use qemu_peek_buffer(), but I think that we used qemu_peek_byte() more to justify the overhead. I am talking from memory here.

vmstate.c static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, · line 175

Quoted 12 lines
vmstate.c +1 -1
index d1f5eb0..b8e6e31 100644
@@ -170,7 +170,7 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
         }
         size = qemu_peek_buffer(f, (uint8_t *)idstr, len, 2);
         if (size != len) {
-            return 0;
+            return -EIO;
         }
         idstr[size] = 0;

This was coded this way on purpose. If we don't have a valid buffer after the subsection identifier, just let the code continue to see if it wasn't a subsection at all. This colud be removed one tested that we don't allow subsections in the middle of a section, only in places where a section can appear.

In general, very nice patch, and fixes the problem.

Later, Juan.

#2 lore raw
Quoted 47 lines · * Juan Quintela ([email protected]) wrote:
Quoted 12 lines · Dr. David Alan Gilbert (git)

From: "Dr. David Alan Gilbert" <[email protected]>

Make qemu_peek_buffer repatedly call fill_buffer until it gets all the data it requires, or until there is an error.

At the moment, qemu_peek_buffer will try one qemu_fill_buffer if there isn't enough data waiting, however the kernel is entitled to return just a few bytes, and still leave qemu_peek_buffer with less bytes than it needed. I've seen this fail in a dev world, and I think it could theoretically fail in the peeking of the subsection headers in the current world.

Quoted 5 lines

Ditto for qemu_peek_byte (which can only be affected due to it's offset).

Simplify qemu_get_buffer since it can now rely on qemu_peek_buffer to loop.

I think this one is wrong, will explain there.

Quoted 2 lines

Use size_t rather than int for size parameters, (and result for those functions that never return -errno).

Nice.

Quoted 20 lines
qemu-file.c +3 -14
-int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size)
+size_t qemu_get_buffer(QEMUFile *f, uint8_t *buf, size_t size)
 {
-    int pending = size;
-    int done = 0;
+    size_t res;
 
-    while (pending > 0) {
-        int res;
+    res = qemu_peek_buffer(f, buf, size, 0);
 
-        res = qemu_peek_buffer(f, buf, pending, 0);
-        if (res == 0) {
-            return done;
-        }
-        qemu_file_skip(f, res);
-        buf += res;
-        pending -= res;
-        done += res;
-    }
Quoted 8 lines

qemu-file.c int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset) · line 743

Quoted 4 lines
qemu-file.c +3 -1
-    return done;
+    qemu_file_skip(f, res);
+
+    return res;

I think this is "theoretical" (a.k.a. no user of this functionality). As this was coded, we could receive buffers bigger than IOBUF_SIZE, with your change, we can't. Just maintating the loop should fix this, right?

Ah, actually that is a good point (I've got a feeling one of my other worlds relies on that); yes, I'll put the loop back and fix it all to be size_t.

Quoted 8 lines

qemu-file.c int qemu_peek_byte(QEMUFile *f, int offset) · line 755

Quoted 4 lines
qemu-file.c +4 -0
+    while (index >= f->buf_size) {
+        int received = qemu_fill_buffer(f);
+
+        if (received <= 0) {

here, I don't know really what to do. We just need one character, so the 1st call to qemu_fill_buffer() gives it to us, or we are already on problems. i.e. no need of the while() loop.

The problem is that peek_byte takes an offset, so while qemu_fill_buffer will get us a byte, we actually need it to get us all the bytes upto the offset, and that's not guaranteed from one call.

Quoted 26 lines

On the other hand, having exactly the same code looks so nice.

At some point I was thinking about making qemu_peek_byte() to use qemu_peek_buffer(), but I think that we used qemu_peek_byte() more to justify the overhead. I am talking from memory here.

vmstate.c static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, · line 175

Quoted 12 lines
vmstate.c +1 -1
index d1f5eb0..b8e6e31 100644
@@ -170,7 +170,7 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
         }
         size = qemu_peek_buffer(f, (uint8_t *)idstr, len, 2);
         if (size != len) {
-            return 0;
+            return -EIO;
         }
         idstr[size] = 0;

This was coded this way on purpose. If we don't have a valid buffer after the subsection identifier, just let the code continue to see if it wasn't a subsection at all. This colud be removed one tested that we don't allow subsections in the middle of a section, only in places where a section can appear.

Quoted 1 line

In general, very nice patch, and fixes the problem.

Thanks, I'll rework and get a V2 up later.

Dave

Quoted 2 lines

Later, Juan.

--
Dr. David Alan Gilbert / [email protected] / Manchester, UK

#3 lore raw
Quoted 5 lines · Dr. David Alan Gilbert
Quoted 5 lines
Quoted 4 lines
qemu-file.c +4 -0
+    while (index >= f->buf_size) {
+        int received = qemu_fill_buffer(f);
+
+        if (received <= 0) {
Quoted 7 lines
Quoted 3 lines

here, I don't know really what to do. We just need one character, so the 1st call to qemu_fill_buffer() gives it to us, or we are already on problems. i.e. no need of the while() loop.

The problem is that peek_byte takes an offset, so while qemu_fill_buffer will get us a byte, we actually need it to get us all the bytes upto the offset, and that's not guaranteed from one call.

that is not a problem.

We never got a "hole" on the things that we ask for. We ask for the "next byte", or the "next bytes", so in qemu_peek_byte() we are guaranteed (with current users) that we would only have to read a single byte.

I.e. qemu_peek_{buffer,byte}(....,offset, size) means that we have "already" peek until offset, and now we want size more bytes.

Later, Juan.

#4 lore raw
Quoted 16 lines · * Juan Quintela ([email protected]) wrote:
Quoted 5 lines · Dr. David Alan Gilbert
Quoted 5 lines
Quoted 4 lines
qemu-file.c +4 -0
+    while (index >= f->buf_size) {
+        int received = qemu_fill_buffer(f);
+
+        if (received <= 0) {
Quoted 7 lines
Quoted 3 lines

here, I don't know really what to do. We just need one character, so the 1st call to qemu_fill_buffer() gives it to us, or we are already on problems. i.e. no need of the while() loop.

The problem is that peek_byte takes an offset, so while qemu_fill_buffer will get us a byte, we actually need it to get us all the bytes upto the offset, and that's not guaranteed from one call.

that is not a problem.

Quoted 7 lines

We never got a "hole" on the things that we ask for. We ask for the "next byte", or the "next bytes", so in qemu_peek_byte() we are guaranteed (with current users) that we would only have to read a single byte.

I.e. qemu_peek_{buffer,byte}(....,offset, size) means that we have "already" peek until offset, and now we want size more bytes.

Hmm ok, that is convenient but is neither documented or enforced; I'll rework my patch and at least document it, and possibly enforce it.

Dave
--
Dr. David Alan Gilbert / [email protected] / Manchester, UK

#5 lore raw
Quoted 22 lines · Dr. David Alan Gilbert
Quoted 21 lines · * Juan Quintela ([email protected]) wrote:
Quoted 5 lines · Dr. David Alan Gilbert
Quoted 5 lines
Quoted 4 lines
qemu-file.c +4 -0
+    while (index >= f->buf_size) {
+        int received = qemu_fill_buffer(f);
+
+        if (received <= 0) {
Quoted 7 lines
Quoted 3 lines

here, I don't know really what to do. We just need one character, so the 1st call to qemu_fill_buffer() gives it to us, or we are already on problems. i.e. no need of the while() loop.

The problem is that peek_byte takes an offset, so while qemu_fill_buffer will get us a byte, we actually need it to get us all the bytes upto the offset, and that's not guaranteed from one call.

that is not a problem.

We never got a "hole" on the things that we ask for. We ask for the "next byte", or the "next bytes", so in qemu_peek_byte() we are guaranteed (with current users) that we would only have to read a single byte.

Quoted 5 lines
Quoted 2 lines

I.e. qemu_peek_{buffer,byte}(....,offset, size) means that we have "already" peek until offset, and now we want size more bytes.

Hmm ok, that is convenient but is neither documented or enforced; I'll rework my patch and at least document it, and possibly enforce it.

thanks very much to take the time to understand it and document it O;-)

Later, Juan.