Edit

IABSD.fr/xenocara/xserver/test/sync/sync.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2025-03-02 09:09:28
    Hash : 3cfba106
    Message : Update to xserver 21.1.16. The security fixes were committed earlier. This is the rest of the 21.1.16 update.

  • xserver/test/sync/sync.c
  • /*
     * Copyright © 2017 Broadcom
     *
     * Permission is hereby granted, free of charge, to any person obtaining a
     * copy of this software and associated documentation files (the "Software"),
     * to deal in the Software without restriction, including without limitation
     * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     * and/or sell copies of the Software, and to permit persons to whom the
     * Software is furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice (including the next
     * paragraph) shall be included in all copies or substantial portions of the
     * Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
     * IN THE SOFTWARE.
     */
    
    #include <assert.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <limits.h>
    #include <xcb/sync.h>
    
    #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
    
    static const int64_t some_values[] = {
            0,
            1,
            -1,
            LLONG_MAX,
            LLONG_MIN,
    };
    
    static int64_t
    pack_sync_value(xcb_sync_int64_t val)
    {
        return ((int64_t)val.hi << 32) | val.lo;
    }
    
    static int64_t
    counter_value(struct xcb_connection_t *c,
                  xcb_sync_query_counter_cookie_t cookie)
    {
        xcb_sync_query_counter_reply_t *reply =
            xcb_sync_query_counter_reply(c, cookie, NULL);
        int64_t value = pack_sync_value(reply->counter_value);
    
        free(reply);
        return value;
    }
    
    static xcb_sync_int64_t
    sync_value(int64_t value)
    {
        xcb_sync_int64_t v = {
            .hi = value >> 32,
            .lo = value,
        };
    
        return v;
    }
    
    /* Initializes counters with a bunch of interesting values and makes
     * sure it comes back the same.
     */
    static void
    test_create_counter(xcb_connection_t *c)
    {
        xcb_sync_query_counter_cookie_t queries[ARRAY_SIZE(some_values)];
    
        for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
            xcb_sync_counter_t counter = xcb_generate_id(c);
            xcb_sync_create_counter(c, counter, sync_value(some_values[i]));
            queries[i] = xcb_sync_query_counter_unchecked(c, counter);
        }
    
        for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
            int64_t value = counter_value(c, queries[i]);
    
            if (value != some_values[i]) {
                fprintf(stderr, "Creating counter with %lld returned %lld\n",
                        (long long)some_values[i],
                        (long long)value);
                exit(1);
            }
        }
    }
    
    /* Set a single counter to a bunch of interesting values and make sure
     * it comes the same.
     */
    static void
    test_set_counter(xcb_connection_t *c)
    {
        xcb_sync_counter_t counter = xcb_generate_id(c);
        xcb_sync_query_counter_cookie_t queries[ARRAY_SIZE(some_values)];
    
        xcb_sync_create_counter(c, counter, sync_value(0));
    
        for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
            xcb_sync_set_counter(c, counter, sync_value(some_values[i]));
            queries[i] = xcb_sync_query_counter_unchecked(c, counter);
        }
    
        for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
            int64_t value = counter_value(c, queries[i]);
    
            if (value != some_values[i]) {
                fprintf(stderr, "Setting counter to %lld returned %lld\n",
                        (long long)some_values[i],
                        (long long)value);
                exit(1);
            }
        }
    }
    
    /* Add [0, 1, 2, 3] to a counter and check that the values stick. */
    static void
    test_change_counter_basic(xcb_connection_t *c)
    {
        int iterations = 4;
        xcb_sync_query_counter_cookie_t queries[iterations];
    
        xcb_sync_counter_t counter = xcb_generate_id(c);
        xcb_sync_create_counter(c, counter, sync_value(0));
    
        for (int i = 0; i < iterations; i++) {
            xcb_sync_change_counter(c, counter, sync_value(i));
            queries[i] = xcb_sync_query_counter_unchecked(c, counter);
        }
    
        int64_t expected_value = 0;
        for (int i = 0; i < iterations; i++) {
            expected_value += i;
            int64_t value = counter_value(c, queries[i]);
    
            if (value != expected_value) {
                fprintf(stderr, "Adding %d to counter expected %lld returned %lld\n",
                        i,
                        (long long)expected_value,
                        (long long)value);
                exit(1);
            }
        }
    }
    
    /* Test change_counter where we trigger an integer overflow. */
    static void
    test_change_counter_overflow(xcb_connection_t *c)
    {
        int iterations = 4;
        xcb_sync_query_counter_cookie_t queries[iterations];
        xcb_void_cookie_t changes[iterations];
        static const struct {
            int64_t a, b;
        } overflow_args[] = {
            { LLONG_MAX, 1 },
            { LLONG_MAX, LLONG_MAX },
            { LLONG_MIN, -1 },
            { LLONG_MIN, LLONG_MIN },
        };
    
        xcb_sync_counter_t counter = xcb_generate_id(c);
        xcb_sync_create_counter(c, counter, sync_value(0));
    
        for (int i = 0; i < ARRAY_SIZE(overflow_args); i++) {
            int64_t a = overflow_args[i].a;
            int64_t b = overflow_args[i].b;
            xcb_sync_set_counter(c, counter, sync_value(a));
            changes[i] = xcb_sync_change_counter_checked(c, counter,
                                                         sync_value(b));
            queries[i] = xcb_sync_query_counter(c, counter);
        }
    
        for (int i = 0; i < ARRAY_SIZE(overflow_args); i++) {
            int64_t a = overflow_args[i].a;
            int64_t b = overflow_args[i].b;
            xcb_sync_query_counter_reply_t *reply =
                xcb_sync_query_counter_reply(c, queries[i], NULL);
            int64_t value = (((int64_t)reply->counter_value.hi << 32) |
                             reply->counter_value.lo);
            int64_t expected_value = a;
    
            /* The change_counter should have thrown BadValue */
            xcb_generic_error_t *e = xcb_request_check(c, changes[i]);
            if (!e) {
                fprintf(stderr, "(%lld + %lld) failed to return an error\n",
                        (long long)a,
                        (long long)b);
                exit(1);
            }
    
            if (e->error_code != XCB_VALUE) {
                fprintf(stderr, "(%lld + %lld) returned %d, not BadValue\n",
                        (long long)a,
                        (long long)b,
                        e->error_code);
                exit(1);
            }
    
            /* The change_counter should have had no other effect if it
             * errored out.
             */
            if (value != expected_value) {
                fprintf(stderr, "(%lld + %lld) expected %lld returned %lld\n",
                        (long long)a,
                        (long long)b,
                        (long long)expected_value,
                        (long long)value);
                exit(1);
            }
    
            free(e);
            free(reply);
        }
    }
    
    static void
    test_change_alarm_value(xcb_connection_t *c)
    {
        xcb_sync_alarm_t alarm = xcb_generate_id(c);
        xcb_sync_query_alarm_cookie_t queries[ARRAY_SIZE(some_values)];
    
        xcb_sync_create_alarm(c, alarm, 0, NULL);
    
        for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
            uint32_t values[] = { some_values[i] >> 32, some_values[i] };
    
            xcb_sync_change_alarm(c, alarm, XCB_SYNC_CA_VALUE, values);
            queries[i] = xcb_sync_query_alarm_unchecked(c, alarm);
        }
    
        for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
            xcb_sync_query_alarm_reply_t *reply =
                xcb_sync_query_alarm_reply(c, queries[i], NULL);
            int64_t value = pack_sync_value(reply->trigger.wait_value);
    
            if (value != some_values[i]) {
                fprintf(stderr, "Setting alarm value to %lld returned %lld\n",
                        (long long)some_values[i],
                        (long long)value);
                exit(1);
            }
            free(reply);
        }
    }
    
    static void
    test_change_alarm_delta(xcb_connection_t *c)
    {
        xcb_sync_alarm_t alarm = xcb_generate_id(c);
        xcb_sync_query_alarm_cookie_t queries[ARRAY_SIZE(some_values)];
    
        xcb_sync_create_alarm(c, alarm, 0, NULL);
    
        for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
            uint32_t mask = XCB_SYNC_CA_TEST_TYPE | XCB_SYNC_CA_DELTA;
            uint32_t test_type = (some_values[i] >= 0 ?
                                   XCB_SYNC_TESTTYPE_POSITIVE_COMPARISON :
                                   XCB_SYNC_TESTTYPE_NEGATIVE_COMPARISON);
            uint32_t values[] = { test_type, some_values[i] >> 32, some_values[i] };
    
            xcb_sync_change_alarm(c, alarm, mask, values);
            queries[i] = xcb_sync_query_alarm_unchecked(c, alarm);
        }
    
        for (int i = 0; i < ARRAY_SIZE(some_values); i++) {
            xcb_sync_query_alarm_reply_t *reply =
                xcb_sync_query_alarm_reply(c, queries[i], NULL);
            int64_t value = pack_sync_value(reply->delta);
    
            if (value != some_values[i]) {
                fprintf(stderr, "Setting alarm delta to %lld returned %lld\n",
                        (long long)some_values[i],
                        (long long)value);
                exit(1);
            }
            free(reply);
        }
    }
    
    int main(int argc, char **argv)
    {
        int screen;
        xcb_connection_t *c = xcb_connect(NULL, &screen);
        const xcb_query_extension_reply_t *ext = xcb_get_extension_data(c, &xcb_sync_id);
    
        if (!ext->present) {
            printf("No XSync present\n");
            exit(77);
        }
    
        test_create_counter(c);
        test_set_counter(c);
        test_change_counter_basic(c);
        test_change_counter_overflow(c);
        test_change_alarm_value(c);
        test_change_alarm_delta(c);
    
        xcb_disconnect(c);
        exit(0);
    }