1. Си / Говнокод #25188

    +3

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    enum {
        HOST = INADDR_LOOPBACK,
        PORT = 6666,
        MAX_BUF = 1024
    };
    
    struct sock {
        int sockfd;
        int addrlen;
        struct sockaddr_in addr;
    } host, client;
    
    int check(int x, char*msg)
    {
        if (!~x) {
            perror(msg);
            exit(1);
        }
        return x;
    }
    
    #define QUOTE_(...) #__VA_ARGS__
    #define QUOTE(...) QUOTE_(__VA_ARGS__)
    #define CHECK(...) check(__VA_ARGS__, QUOTE(line __LINE__: __VA_ARGS__))
    
    int main(int argc, char**argv)
    {
        struct sock host, client;
        host.sockfd = CHECK(socket(AF_INET, SOCK_STREAM, 0)),
        host.addr = (struct sockaddr_in){AF_INET, htons(PORT), htonl(HOST)};
        CHECK(bind(host.sockfd, &(struct sockaddr)host.addr, sizeof(host.addr)));
        CHECK(listen(host.sockfd, 1));
        CHECK(client.sockfd = accept(host.sockfd, (void*)&client.addr, &client.addrlen));
        printf("connected: %s\n", inet_ntoa(client.addr.sin_addr));
        
        struct {int len; char buf[MAX_BUF];} msg;
        while (CHECK(msg.len = recv(client.sockfd, msg.buf, MAX_BUF - 1, 0)) && msg.len) {
            msg.buf[msg.len] = 0;
            printf("%s", msg.buf);
            send(client.sockfd, msg.buf, msg.len, 0);
        }
        close(client.sockfd);
        close(host.sockfd);
        return 0;
    }

    Почему если закоментить 36-ю строчку адрес килента 0.0.0.0?

    Запостил: 666_N33D135, 13 Декабря 2018

    Комментарии (63) RSS

    • #c #socket
      Ответить
    • Потому что
      struct sock {
          int sockfd;
          int addrlen;
          struct sockaddr_in addr;
      } host, client;
      Ответить
      • struct cock {
            int comb;
            int wattles;
            int beak;
            struct feather plumage;
        } rooster, chicken;
        Ответить
      • показать все, что скрытоvanished
        Ответить
        • Тут вот что интересно: в глобальном контексте (что обычно кладётся в секцию .data) объявлено:
          struct sock {
              int sockfd;
              int addrlen;
              struct sockaddr_in addr;
          } host, client;


          Переменные с теми же самыми именами объявлены в функции main (что обычно кладётся в стек):
          struct sock host, client;


          Если убрать локальное объявление (ту самую 36-ю строку), то у нидлеса программа не может определить адрес клиента.
          Ответить
          • > не может определить адрес клиента
            И правильно делает! Ибо accept'у некуда писать этот адрес. В отличие от случая с локалкой где всё случайно работает из-за UB'а.
            Ответить
            • показать все, что скрытоvanished
              Ответить
              • > int putix; // pitux == 0
                В глобалке - ноль. В локалке - UB.

                Инициализируйте переменные, блеать.

                The addrlen argument is a value-result argument: the caller must initialize it to contain the size (in bytes) of the structure pointed to by addr; on return it will contain the actual size of the peer address.
                Ответить
                • показать все, что скрытоvanished
                  Ответить
                  • Всё хуже. 169.253.UB.UB.

                    The returned address is truncated if the buffer provided is too small; in this case, addrlen will return a value greater than was supplied to the call.
                    Ответить
                    • показать все, что скрытоvanished
                      Ответить
                      • Нет.

                        Х.з., сомнительный ворнинг так то (у студии есть, емнип). Из хедеров много всякого говна торчит, которое ты никогда юзать не будешь. Запрещать все эти имена юзать в качестве локалок, имхо, оверкилл.

                        А вот в пределах одной функции можно было бы и показать, это на 99% залёт. Но тоже нет.
                        Ответить
                • Сапасибо, я видимо этот пукт упустил из виду.
                  client.addrlen = sizeof(struct sockaddr);
                  Теперь всё работает.
                  Ответить
            • >> В отличие от случая с локалкой

              В обоих случаях результат зависит от деталей реализации.

              1. Глобальные переменные.

              У секции .data есть инициализированная часть, лежащая в экзешнике, которая забита нулями и прочими начальными значениями (константами и правой частью от выражений типа int x = 42;), и неинициализированная часть, которая создаётся после загрузки (чем она забита, решает загрузчик экзешника).

              Кудкудкудкуда ляжет переменная (в инициализированную часть или в неинициализированную), решают кококонпелятор и линкер.

              2. Локальные переменные.

              Тут всё хуже. В стек может наложить кто угодно. Я помню, как Царь полагался на то, что его промежуточные данные в стеке между вызовами функций сохраняются, и его программа обломалась в Ideone, где какой-то патч типа Propolice или Stackguard чистит стек вилкой.

              Значит, у Нидлеса вариант с локальными переменными работает только потому, что по счастливой случайности кто-то в эту область стека положил ненулевые значения.
              Ответить
              • > от деталей реализации
                6.7.8 Initialization
                10) <...> If an object that has static storage duration is not initialized explicitly, then <...> if it has arithmetic type, it is initialized to (positive or unsigned) zero.
                Ответить
                • То есть если операционка не справляется, то рантайм должен занулить как минимум все переменные загадочного арифметического типа?

                  А с «неарифметическими» что будет?
                  Ответить
                  • - if it has pointer type, it is initialized to a null pointer;
                    - if it has arithmetic type, it is initialized to (positive or unsigned) zero;
                    - if it is an aggregate, every member is initialized (recursively) according to these rules;
                    - if it is a union, the first named member is initialized (recursively) according to these rules.


                    Но это всё касается только static storage duration, на локалки это правило не действует и они наполнены UB'ами.
                    Ответить
                    • >> if it is a union, the first named member is initialized

                      А если другие члены окажутся длиннее первого, то их хвосты можно не инициализировать?
                      Ответить
                      • Очевидно да. Паддинги тоже можно не инициализировать. Само собой, на практике всё это будет забито нулями, просто без гарантий.

                        > если операционка не справляется
                        Ну это странно... У процесса своей грязной памяти ещё нету, а брать её из других процессов не почистив вилкой - ебучее решето. Хотя какая-нибудь мелкая ось, у которой всё доверенное и без изоляции, в принципе, имеет право.
                        Ответить
                    • показать все, что скрытоvanished
                      Ответить
                      • > когда эти все типы являются членами union'а
                        Накладок не будет т.к. только первый именованный из них обязан инициализироваться.

                        > использовать не 754
                        Походу да:
                        Annex F (normative) IEC 60559 floating-point arithmetic
                        F.1 Introduction
                        1) <...> An implementation that defines __STDC_IEC_559__ shall conform to the specifications in this annex.

                        Другим плавучим питухам просто нельзя объявлять это макро.
                        Ответить
                        • Проверю каких-нибудь случайных плавучих питухов:

                          1. MBF (Microsoft binary format, BASIC). Порядок со смещением 128, причём для чисел любой точности. Старший разряд мантиссы подразумевается равным единице, как у IEEE 754. Denormal, inf, NaN не поддерживаются. Знаковый бит где-то в середине.

                          Если все биты обнулены, то хранится +0.

                          2. HFP (hexadecimal floating point, IBM System/360). Порядок со смещением 64 для чисел любой точности, основание порядка равно 16, а не 2 (поэтому старший подразумеваемый разряд мантиссы равен нулю). Знаковый бит в самом начале.

                          Если все биты обнулены, то хранится +0.

                          3. 48-битный Real в Трубопаскакале. Порядок хранится со смещением 128. Мантисса записывается задом наперёд. Знаковый бит где-то в середине. Старший разряд мантиссы подразумевается равным единице, как у IEEE 754.

                          Если все биты обнулены, то хранится +0.

                          Найденные питухи легко обнулять. Нужно найти какой-нибудь посложнее.

                          У БЭСМ порядок хранился без смещения (т. е. нули соответствовали не минимальному значению, а нулевой степени). Но я пока ещё не прочитал, как там хранилась мантисса.
                          Ответить
                          • У БЭСМ-6 порядок уже со смещением 64, т. е. для малых чисел в машинном представлении нули. А вот мантисса хранилась в дополнительном коде (two's complement), как у целого питуха. Подразумеваемой единицы не было.

                            В любом случае, у БЭСМ-6 ноль состоял из нулей. А вот минус нуля (судя по дополнительному коду) не было.

                            P.S. Какие ещё плавпитухи бывают?

                            P.P.S. В IEEE 754 описан ещё двоично-десятичный питух. Строится он по тому же принципу, что и двоичный, только бывают ненормальные кодировки порядка для экономии битов.
                            Ответить
                    • показать все, что скрытоvanished
                      Ответить
                  • > загадочного арифметического типа
                    6.2.5 Types
                    18) Integer and floating types are collectively called arithmetic types.
                    Ответить
                  • > операционка не справляется
                    Или её ещё нет или вообще нет, а сишку юзать хочется...
                    Ответить
                    • Помню прошивку, написанную на С++. Она генерировалась из exe-шника, но точка входа устанавливалась на main. Поэтому конструкторы для глобальных объектов не вызывались... и ненулевой инициализации глобальных/статических переменных, кажется, тоже не было.
                      Ответить
            • > accept'у некуда писать этот адрес
              всмысле некуда? Я же передал укозатель на сьруктуру?
              Ответить
              • RTFM.Я выше привел цитату из мана почему твой код не работает.

                З.Ы. Первый вопрос, который должен возникать при вызове незнакомой функции - "а где она берёт длину буфера?"
                Ответить
                • > а где она берёт длину буфера?
                  Я думал она знает размер сьруктуры.
                  Ответить
                  • Размер структуры она знает. А вот размер твоего буфера - нет. Ну и подразумеваемого размера нету т.к. адреса могут быть разного типа.

                    А вслепую вроде только gets (функция, запрещённая к использованию) пишет.
                    Ответить
                    • > только gets
                      ещё scanf("%s", &s)
                      Ответить
                      • В "PHP" функции fgets, fread, fscanf сами выделяют память. Именно поэтому я за "PHP".
                        Ответить
        • > там на самом деле sockaddr а не sockaddr_in
          sockaddr_in чтоб кастовать меньше
          Ответить
    • Продолжим ревью.

      > if (!~x)
      Это что за покемон? Хакерская проверка на -1? Ещё не хватало у знакового числа биты инвертировать...

      > && msg.len
      Недостижимый код, у тебя CHECK и так вернёт 0. while (msg.len = CHECK(recv(...)), имхо, было бы немного наглядней.

      > send(client.sockfd, msg.buf, msg.len, 0);
      Уверен, что улетит одним куском? Так можно и байты проебать. Ну и CHECK забыл.
      Ответить
      • показать все, что скрытоvanished
        Ответить
      • > покемон?
        но он такой маленький няшка, как его не вставить? ^_^

        > Ещё не хватало у знакового числа биты инвертировать
        а знаковость как на побитловые опереции влияет?

        > Недостижимый код
        изначально были проверки на -1 без функции, там нужно было, так и осталось.

        > улетит одним куском?
        на холокосте да, тестовая программка же.
        Ответить
        • > знаковость
          Стандарта под рукой нет, но у меня ощущение, что там возможен UB.
          Ответить
          • 4 The result of the ~ operator is the bitwise complement of its (promoted) operand (that is,
            each bit in the result is set if and only if the corresponding bit in the converted operand is
            not set). The integer promotions are performed on the operand, and the result has the
            promoted type. If the promoted type is an unsigned type, the expression ~E is equivalent
            to the maximum value representable in that type minus E.


            Обычная побитовая инверсия.
            Ответить
            • C11 6.5.4 [...] These operators yield values that depend on the internal representations of integers, and have implementation-defined and undefined aspects for signed types.

              Педанты могут пройтись по упоминаниям sign bit, trap (representation), negative zero и какие там ещё ужасы. Но !~x того не стоит.
              Ответить
    • показать все, что скрытоvanished
      Ответить

    Добавить комментарий