Skip to content

Avoid false positive out-of-bounds in writeForgottenNodePingExt#11053

Merged
madolson merged 5 commits intoredis:unstablefrom
enjoy-binbin:fix_false_positive
Jul 28, 2022
Merged

Avoid false positive out-of-bounds in writeForgottenNodePingExt#11053
madolson merged 5 commits intoredis:unstablefrom
enjoy-binbin:fix_false_positive

Conversation

@enjoy-binbin
Copy link
Contributor

@enjoy-binbin enjoy-binbin commented Jul 28, 2022

In clusterMsgPingExtForgottenNode, sizeof(name) is CLUSTER_NAMELEN,
and sizeof(clusterMsgPingExtForgottenNode) is > CLUSTER_NAMELEN.
Doing a (name + sizeof(clusterMsgPingExtForgottenNode)) sanitizer
generates an out-of-bounds error which is a false positive in here.

Reported in test-sanitizer-undefined (clang):

runtime error: index 48 out of bounds for type 'char [40]'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior

In this fix, we are using ext+1 instead of ext->name+sizeof(clusterMsgPingExtForgottenNode)
to avoid false positive sanitizer out-of-bounds error. Also convert writeHostnamePingExt.

introduced in #10869

In clusterMsgPingExtForgottenNode, sizeof(name) is CLUSTER_NAMELEN,
and sizeof(clusterMsgPingExtForgottenNode) is > CLUSTER_NAMELEN.
Doing a (name + sizeof(clusterMsgPingExtForgottenNode)) sanitizer
generates an out-of-bounds error which is a false positive in here.
@enjoy-binbin
Copy link
Contributor Author

@oranagra
Copy link
Member

there's something i'm still missing.

we're talking of this line, right:

    *cursor = (clusterMsgPingExt *) (ext->name + sizeof(clusterMsgPingExtForgottenNode));

ext is of type clusterMsgPingExtForgottenNode declared as:

typedef struct {
    char name[CLUSTER_NAMELEN]; /* Node name. */
    uint64_t ttl; /* Remaining time to blacklist the node, in seconds. */
} clusterMsgPingExtForgottenNode;

so if ext->name is is CLUSTER_NAMELEN, and ext is 8 bytes bigger, this line reaches beyond name and actually also beyond ext.
actually, considering that name is the first member, the result is the same as doing *cursor = etx+1.
was that what we meant? i don't see that it's allocated as an array..

@madolson please have a look.

@oranagra
Copy link
Member

ohh, i missed the fact it is casted to clusterMsgPingExt, and all of this math:

    estlen = sizeof(clusterMsg) - sizeof(union clusterMsgData);
    estlen += (sizeof(clusterMsgDataGossip)*(wanted + pfail_wanted));
    estlen += getHostnamePingExtSize();
    estlen += dictSize(server.cluster->nodes_black_list) *
        (sizeof(clusterMsgPingExt) + sizeof(clusterMsgPingExtForgottenNode));

    /* Note: clusterBuildMessageHdr() expects the buffer to be always at least
     * sizeof(clusterMsg) or more. */
    if (estlen < (int)sizeof(clusterMsg)) estlen = sizeof(clusterMsg);
    buf = zcalloc(estlen);

well, maybe we can declare a struct for all of that instead messing with offsets?

@oranagra
Copy link
Member

@enjoy-binbin can you check if doing this will also solve the problem (i.e. avoid using name)?

    *cursor = (clusterMsgPingExt *) (ext + sizeof(clusterMsgPingExtForgottenNode));

i rather not silence warnings in that area since it has a chance to hide other issues, and that code seems dangerous so i rather not do that.

@enjoy-binbin
Copy link
Contributor Author

@enjoy-binbin
Copy link
Contributor Author

i rather not silence warnings in that area since it has a chance to hide other issues, and that code seems dangerous so i rather not do that.

agree. my first thinking is also use ext, but I don't really want to modify the code, it is following the writeHostnamePingExt.
anyway, i see using ext can also solve the problem (the daily passed), i'll modify it first

int writeHostnamePingExt(clusterMsgPingExt **cursor) {
    /* If hostname is not set, we don't send this extension */
    if (sdslen(myself->hostname) == 0) return 0;

    /* Add the hostname information at the extension cursor */
    clusterMsgPingExtHostname *ext = &(*cursor)->ext[0].hostname;
    memcpy(ext->hostname, myself->hostname, sdslen(myself->hostname));
    uint32_t extension_size = getHostnamePingExtSize();

    /* Move the write cursor */
    (*cursor)->type = htons(CLUSTERMSG_EXT_TYPE_HOSTNAME);
    (*cursor)->length = htonl(extension_size);
    /* Make sure the string is NULL terminated by adding 1 */
    *cursor = (clusterMsgPingExt *) (ext->hostname + EIGHT_BYTE_ALIGN(sdslen(myself->hostname) + 1));
    return extension_size;
}

Copy link
Member

@oranagra oranagra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@madolson please have another set of eyes on this as it's not my domain.

@oranagra
Copy link
Member

Copy link
Contributor

@madolson madolson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this code could probably use 1) More documentation or 2) a better approach to making sure the offsets are correct. This does look OK.

@madolson madolson merged commit 90f35ce into redis:unstable Jul 28, 2022
@enjoy-binbin enjoy-binbin deleted the fix_false_positive branch July 28, 2022 22:53
enjoy-binbin added a commit to enjoy-binbin/redis that referenced this pull request Jul 31, 2023
…s#11053)

In clusterMsgPingExtForgottenNode, sizeof(name) is CLUSTER_NAMELEN,
and sizeof(clusterMsgPingExtForgottenNode) is > CLUSTER_NAMELEN.
Doing a (name + sizeof(clusterMsgPingExtForgottenNode)) sanitizer
generates an out-of-bounds error which is a false positive in here
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants