REBOL3 tracker
  0.9.12 beta
Ticket #0001816 User: anonymous

Project:



rss
TypeBug Statusbuilt Date10-Jan-2011 01:17
Versionalpha 110 CategoryHost-Kit Submitted byKaj
PlatformLinux x86 libc6 Severityminor Priorityhigh

Summary RL_GET_STRING computes wrong address from incompatible item size
Description If you give an index to RL_GET_STRING on a series of single bytes, it will return an address computed as if the items are double bytes.

I'd guess RL_GET_STRING is currently hardcoded to always count in double bytes.
Example code
length = - RL_GET_STRING(binary, index, (void **) &pointer);

Assigned ton/a Fixed inalpha 111 Last Update31-Jan-2011 01:04


Comments
(0003014)
oldes
13-Jan-2011 23:10

Maybe we could have RL_GET_BINARY, couldn't we? See also http://issue.cc/r3/1823
(0003026)
Carl
17-Jan-2011 04:25

It's not hard coded. There must be a problem with how the string was created. Can you provide the full example?
(0003027)
Kaj
17-Jan-2011 17:53

Here's the callback where RL_GET_STRING is used, with a workaround in place:

// Callback for filling the buffer for writing

size_t read_buffer(char *buffer, size_t width, size_t chunks, void *session) {
    char *data;
    u32 tail, length = chunks * width;

//    R3 A110 FIXME: address miscomputed as if items are double bytes:
//    tail = - RL_GET_STRING(((struct session *) session)->output, ((struct session *) session)->progress, (void **) &data);
    tail = - RL_GET_STRING(((struct session *) session)->output, 0, (void **) &data) - ((struct session *) session)->progress;

    if (tail < length) length = tail;

//    memcpy(buffer, data, length); // Thread safe?
    memcpy(buffer, data + ((struct session *) session)->progress, length);
    ((struct session *) session)->progress += length;

    return length;
}

The series and session record are set up as follows:

struct session {
    CURL *handle;
    CURLcode status;

    REBSER *input;

    REBSER *output;
    u64 progress;

    struct curl_slist *headers;

    // Progress callback
    REBSER *context;
    u32 word;
};

...

    case 7: { // write
        session = RXA_HANDLE(arguments, 1);
        handle = session->handle;

        if ((series = session->output) != NULL) RL_PROTECT_GC(series, 0);
        session->output = series = RXA_SERIES(arguments, 2);
        RL_PROTECT_GC(series, 1);

        session->progress = RXA_INDEX(arguments, 2);

        if (status = curl_easy_setopt(handle, CURLOPT_READDATA, session)) break;
        if (status = curl_easy_setopt(handle, CURLOPT_READFUNCTION, read_buffer)) break;

        ...
(0003032)
Kaj
18-Jan-2011 23:41

The binary that was passed to the WRITE command where the corruption was observed came from READ FILE, so maybe the problem is in there.
(0003034)
Kaj
18-Jan-2011 23:50

Here's the interface description of the command:

write: command [{Prepare write action.} session [handle!] data [binary!]]

So we should really be getting a binary. The series is protected because the buffer is actually written in later command calls. No reference to it survives in the REBOL program, so it is not changed to a string inbetween or something like that. Also, with the workaround, the written files are correct, so they are really single byte.
(0003046)
oldes
25-Jan-2011 20:45

Maybe to make it more clear...

srcLen = - RL_GET_STRING(ser, RXA_INDEX(frm, 1), (void **)&srcData);
srcData -= RXA_INDEX(frm, 1); //<--- NOTE that you must correct the pointer

RL_GET_STRING multiplies the second (index) argument by 2.
(0003047)
oldes
25-Jan-2011 20:59

Also there may be problem the fact that when you create binary on rebol side, RXI_SER_WIDE is always set to 1.

...
"export bin-test: command [bin [binary!]]\n"
...
case 1: {
char *srcData;
u32 srcLen;
REBSER *ser = RXA_SERIES(frm, 1);
RL_PRINT("RXI_SER_WIDE=%d\n", RL_SERIES(ser, RXI_SER_WIDE));
RL_PRINT("RXI_SER_SIZE=%d\n", RL_SERIES(ser, RXI_SER_SIZE));
RL_PRINT("RXI_SER_LEFT=%d\n", RL_SERIES(ser, RXI_SER_LEFT));
srcLen = - RL_GET_STRING(ser, RXA_INDEX(frm, 1), (void **)&srcData);
//srcData-= RXA_INDEX(frm, 1);
RL_PRINT("index=%d\n", RXA_INDEX(frm, 1));
RL_PRINT("srcLen=%d\n", srcLen);
return RXR_NONE;
}
...
bin-test #{78DAF348CDC9C90700058C01F5}
bin-test next #{78DAF348CDC9C90700058C01F5}
bin-test to-binary "hello"
>>
RXI_SER_WIDE=1
RXI_SER_SIZE=16
RXI_SER_LEFT=2
index=0
srcLen=13
RXI_SER_WIDE=1
RXI_SER_SIZE=16
RXI_SER_LEFT=2
index=1
srcLen=12
RXI_SER_WIDE=1
RXI_SER_SIZE=8
RXI_SER_LEFT=2
index=0
srcLen=5
(0003049)
abolka
25-Jan-2011 22:21

You probably should not use RL_GET_STRING to access the data of a binary!, but RL_SERIES(series, RXI_SER_DATA) instead.
(0003062)
Carl
31-Jan-2011 01:04

The size check logic was reversed, so this function wasn't working correctly at all. It's fixed now in A111.

Date User Field Action Change
31-Jan-2011 01:04 carl Comment : 0003062 Added -
31-Jan-2011 01:02 carl Fixedin Modified => alpha 111
31-Jan-2011 01:02 carl Status Modified reviewed => built
25-Jan-2011 22:21 abolka Comment : 0003049 Added -
25-Jan-2011 21:02 oldes Comment : 0003047 Modified -
25-Jan-2011 21:02 oldes Comment : 0003047 Modified -
25-Jan-2011 20:59 oldes Comment : 0003047 Added -
25-Jan-2011 20:45 oldes Comment : 0003046 Added -
18-Jan-2011 23:50 Kaj Comment : 0003034 Added -
18-Jan-2011 23:41 Kaj Comment : 0003032 Added -
17-Jan-2011 17:53 Kaj Comment : 0003027 Added -
17-Jan-2011 04:25 carl Comment : 0003026 Added -
17-Jan-2011 04:24 carl Description Modified -
17-Jan-2011 04:24 carl Status Modified submitted => reviewed
13-Jan-2011 23:10 oldes Comment : 0003014 Added -
10-Jan-2011 01:17 Kaj Ticket Added -