Перейти к содержанию
    

LwIP это потенциально опасное ПО или нет?

Вот если я скажем делаю так (использую NetConn API):

struct netconn *conn, *newconn;
err_t err, accept_err;

conn = netconn_new(NETCONN_TCP);

while (1)
{
    if (conn != NULL)
    {
        err = netconn_bind(conn, NULL, 7000);

        if (err == ERR_OK)
        {
            netconn_listen(conn);

            while (1)
            {
                accept_err = netconn_accept(conn, &newconn);

                /* Process the new connection. */
                if (accept_err == ERR_OK)
                {
                    ...
                }
            }
        }
        else
        {
            netconn_delete(conn);
        }
    }
}

Т.е. все "по мануалу" и вопросов тут нет. Но вот вопрос такой тут - в функцию netconn_accept(conn, &newconn); я передал указатель на newconn, а эта функция в ответ обновила его значением указателя на свою локальную переменную (как я понял). Т.е. др. словами говоря - она создала внутри у себя какую-то локальную переменную, а потом второй параметр этой самой функции обновила уже новым значением указателя на эту переменную. И как теперь это понимать - ведь та локальная переменная существует в куче, но может же в любой момент времени быть затерта. Т.е. все работает как бы "до поры - до времени" так получается? Пока не "затрется случайно" та локальная переменная другим чем-то в ОЗУ. Или я не так понял? Или тут эта переменная newconn как бы "неполноценно" используется и в ней только что-то одно временно берется и потому все безопасно работает? Спасибо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я после того как написал свой вопрос все переживал, что не привел код самой функции netconn_accept() из стека LwIP и думал "вот люди будут меня ругать, что не показал ее код тоже", а оказывается никто не ответил даже еще. Вот сама функция netconn_accept из стека (в ней я убрал все некомпилирующиеся куски кода из-за условной компиляции чтобы не загромождать лишним кодом, а также добавил комментарий в том месте где ожидается получение семафора - см. код):

err_t netconn_accept(struct netconn *conn, struct netconn **new_conn)
{
    struct netconn *newconn;
    err_t err;


    LWIP_ERROR("netconn_accept: invalid pointer", (new_conn != NULL), return ERR_ARG;);
    *new_conn = NULL;
    LWIP_ERROR("netconn_accept: invalid conn", (conn != NULL), return ERR_ARG;);
    LWIP_ERROR("netconn_accept: invalid acceptmbox", sys_mbox_valid(&conn->acceptmbox), return ERR_ARG;);

    err = conn->last_err;
    if (ERR_IS_FATAL(err))
    {
        /* don't recv on fatal errors: this might block the application task
         waiting on acceptmbox forever! */
        return err;
    }

    /* т.к. 3-й параметр тут - timeout равен 0, то ждем бесконечно долго прихода message (так сделано в этой функции): */
    sys_arch_mbox_fetch(&conn->acceptmbox, (void **) &newconn, 0);

    /* Register event with callback */
    API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);

    if (newconn == NULL)
    {
        /* connection has been aborted */
        NETCONN_SET_SAFE_ERR(conn, ERR_ABRT);
        return ERR_ABRT;
    }

    *new_conn = newconn;
    /* don't set conn->last_err: it's only ERR_OK, anyway */
    return ERR_OK;
}

Обратите внимание на строку №<последняя строка - 3> тут - вот это и есть, то о чем я писал:

    *new_conn = newconn;

т.е. приравнивается значение указателя из 2-го параметра значению указателя на локальную переменную структуры!

 

Т.е. вопрос такой в итоге (извините, что сложно может написал до этого) - "Почему эта функция netconn_accept() из стека LwIP возвращает фактически указатель на свою локальную переменную?? Это правильно??" Спасибо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Т.е. вопрос такой в итоге (извините, что сложно может написал до этого) - "Почему эта функция netconn_accept() из стека LwIP возвращает фактически указатель на свою локальную переменную?? Это правильно??" Спасибо.

Она возвращает значение этой локальной переменной, а не указатель на нее

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Она возвращает значение этой локальной переменной, а не указатель на нее

Нет не так. В эту функцию передается указатель на указатель - переменная new_conn и вот такое объявление ее как параметра функции:

struct netconn **new_conn

Внутри функции объявляется свой, локальный указатель на структуру newconn (может тут кроется ответ на загадку т.к. при его объявлении выделяется память из какой-то "особой" памяти самого стека LwIP? но это бред все-таки). Далее, под конец, мы видим, что:

*new_conn = newconn;

т.е. все как писал - по переданному указателю на указателю пишется значение своего локального указателя. Тут даже и пояснять мне уже не нужно т.к. может запутываю в итоге и непонятен будет вопрос - все и так видно как на ладони.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

...по переданному указателю на указателю пишется значение своего локального указателя.

Так в чем опасность? Сами же написали значение, не указатель.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Так в чем опасность? Сами же написали значение, не указатель.

Возвращается обратно значение указателя. А через это значение указателя далее в коде (в вызвавшей функции) идут обращения к той структуре (см. код). Т.е. где-то в куче она все еще есть пока что! Т.е. обращаемся к ней, а она находится в куче (или же в "бывшей" памяти стека) в это время. Вот так все и выходит. Теперь надеюсь понятно да? Если теперь опять какую-то (любую) функцию вызовем, то она легко может затереть значение той переменной в RAM, а мы по-прежнему будем думать, что то значение валидное внутри вызывающей основной функции. У меня еще есть объяснение, что вот с точки зрения применения ОС (NetConn API только для ОС написано) тут вроде ничего "страшного" нет. Вроде бы! У каждой задачи свой стек и т.д. И поэтому эти стеки задач существуют в раздельности и нерушимы. Но на самом деле - если, как только что написал, снова вызовем какую-то любую функцию то значение структуры все равно покоцается в этом "нерушимом" стеке. Или опять-таки я не прав и все не так? И вообще зачем они так это сделали по-дурацки (так выходит)? Зачем этот LwIP себе еще отдельные порции RAM "отгребает" при инициализации? И не малые причем! Это все отдельные и другие вопросы (их много вообще-то). Но пока хотя бы на этот вопрос в теме кто бы ответил.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

AleksBak:

 

Представьте, Вам нужно написать функцию, которая будет внутри использовать malloc для выделения памяти.

 

Но указатель на выделенную память нужно вернуть не через возвращаемое значение, а через один из аргументов функции.

 

Подумайте, как бы Вы написали такую функцию -- уверен, это поможет найти ответ на Ваш вопрос :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

...

Представьте, Вам нужно написать функцию, которая будет внутри использовать malloc для выделения памяти.

...

malloc-ом я память выделяю и сразу же дальше использую внутри функции. А тут в одной функции выделена память, создана переменная в этой памяти и возвращается указатель на эту созданную переменную. А в вызвавшей функции мы продолжаем обращаться к той переменной несмотря на то, что она когда-то была простой локальной переменной в той функции, а не у нас и не здесь. Так теперь понятно? Это же разные вещи тут. Еще вот объяснение у меня есть - может в этом LwIP не настолько все просто сделано и этот указатель, который возвращается указывает на какую-то особую "LwIP"-овскую суперпамять внутри RAM? Но что-то по коду такого не видно тут такого абсолютно. Надо бы отладчиком посмотреть, что и как когда будет возможность.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

...Так теперь понятно?...

Мне точно понятно, что Вы прочитали не всё моё сообщение :)

 

Но указатель на выделенную память нужно вернуть не через возвращаемое значение, а через один из аргументов функции.

Т.е. цель -- написать функцию-обёртку для malloc, которая будет не просто использовать память, а возвращать её наружу (и именно через аргумент).

 

Всё-таки советую Вам написать такую функцию :)

А уже потом, если вопросы всё-таки останутся, то обсудить их.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

...написать функцию-обёртку для malloc, которая будет не просто использовать память, а возвращать её наружу (и именно через аргумент).

Всё-таки советую Вам написать такую функцию :)....

Да я Вам сразу отвечу как бы все это было бы. Что тут размышлять как и чего. Выделил память, ничего бы с ней не делал дальше и тут же вернул бы на нее указатель. И Все. А тут - выделяется память, в ней создается переменная, что-то с ней делается, а потом возвращается указатель на ту переменную, а далее мы у себя опять обращаемся к этой переменной откуда-то в куче (в мусорке), а если еще какую-то функцию вызовем по дороге вдруг неожиданно, то ничего не останется от той переменной в мусорке, но будем все равно к ней обращаться т.к. так учат нас.

 

Еще добавлю - где-то даже кажется есть строгое указание в рекомендациях, что так делать категорически не рекомендуется. Вот только не помню где это и кто бы подсказал бы тут.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да я Вам сразу отвечу как бы все это было бы. Что тут размышлять как и чего.

 

Это Ваш выбор :)

 

Вы просили помощи, я пытался Вам помочь :)

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Это Ваш выбор :)

 

Вы просили помощи, я пытался Вам помочь :)

Ну да - спасибо и на этом. Кстати тут вообще-то ничего не выделяется - в смысле это не какая-то ненужная обертка для функции mallocc как Вы пишите с самого начала, а я пытаюсь Вам отвечать согласно такой теории и в итоге не прав оказываюсь. Здесь просто: 1. создается локальная переменная; 2. что-то делается; 3. и возвращается потом указатель на эту локальную переменную. Вот так правильное описание, что тут происходит.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я правильно понимаю, что newconn во втором сообщении обретает значение при вызове sys_arch_mbox_fetch? Если да - то что говорит дока на эту функцию по поводу способа выделения памяти для этой структуры и последующего ее освобождения?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я правильно понимаю, что newconn во втором сообщении обретает значение при вызове sys_arch_mbox_fetch?...

Эта структура newconn вот такая - см. ее объявление ниже и на выходе из функции в ней должны появится готовые уже заполненные поля. А в вызвавшей, основной функции мы уже пользуемся этим "готовым" набором (см. коммент в 1-м куске кода: "Process the new connection."):

/** A netconn descriptor */
struct netconn
{
/** type of the netconn (TCP, UDP or RAW) */
enum netconn_type type;
/** current state of the netconn */
enum netconn_state state;
/** the lwIP internal protocol control block */
union
{
	struct ip_pcb *ip;
	struct tcp_pcb *tcp;
	struct udp_pcb *udp;
	struct raw_pcb *raw;
} pcb;
/** the last error this netconn had */
err_t last_err;
/** sem that is used to synchroneously execute functions in the core context */
sys_sem_t op_completed;
/** mbox where received packets are stored until they are fetched
 by the netconn application thread (can grow quite big) Это QueueHandle_t. */
sys_mbox_t recvmbox;
#if LWIP_TCP
/** mbox where new connections are stored until processed by the application thread Это QueueHandle_t. */
sys_mbox_t acceptmbox;
#endif /* LWIP_TCP */
/** only used for socket layer */
#if LWIP_SOCKET
int socket;
#endif /* LWIP_SOCKET */
#if LWIP_SO_SNDTIMEO
/** timeout to wait for sending data (which means enqueueing data for sending in internal buffers) */
s32_t send_timeout;
#endif /* LWIP_SO_RCVTIMEO */
#if LWIP_SO_RCVTIMEO
/** timeout to wait for new data to be received (or connections to arrive for listening netconns) */
int recv_timeout;
#endif /* LWIP_SO_RCVTIMEO */
#if LWIP_SO_RCVBUF
/** maximum amount of bytes queued in recvmbox not used for TCP: adjust TCP_WND instead! */
int recv_bufsize;
/** number of bytes currently in recvmbox to be received,
 tested against recv_bufsize to limit bytes on recvmbox
 for UDP and RAW, used for FIONREAD */
s16_t recv_avail;
#endif /* LWIP_SO_RCVBUF */
/** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */
u8_t flags;
#if LWIP_TCP
/** TCP: when data passed to netconn_write doesn't fit into the send buffer,
 this temporarily stores how much is already sent. */
size_t write_offset;
/** TCP: when data passed to netconn_write doesn't fit into the send buffer,
 this temporarily stores the message.
 Also used during connect and close. */
struct api_msg_msg *current_msg;
#endif /* LWIP_TCP */
/** A callback function that is informed about events for this netconn */
netconn_callback callback;
};

...Если да - то что говорит дока на эту функцию по поводу способа выделения памяти для этой структуры и последующего ее освобождения?

Не знаю. Я новичок тут и поэтому спросил как оно работает и почему так сделано.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3. и возвращается потом указатель на эту локальную переменную.
Где? Я вижу, что в процессе "2. что-то делается" в эту локальную переменную заносится указатель на созданный в куче объект. Потом этот указатель записывается в то место, которое было указано входным параметром. Что до вас и пытался донести des333.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...