xiaoxiang781216 commented on a change in pull request #5498:
URL: https://github.com/apache/incubator-nuttx/pull/5498#discussion_r810169659



##########
File path: sched/task/task_tls_alloc.c
##########
@@ -0,0 +1,113 @@
+/****************************************************************************
+ * sched/task/task_tls_alloc.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <sched.h>
+#include <errno.h>
+
+#include <nuttx/tls.h>
+#include <nuttx/semaphore.h>
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#if CONFIG_TLS_TASK_NELEM > 0
+
+static int g_tlsset = 0;
+static sem_t g_tlssem = SEM_INITIALIZER(1);
+static tls_dtor_t g_tlsdtor[CONFIG_TLS_TASK_NELEM];
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int task_tls_alloc(tls_dtor_t dtor)
+{
+  int ret;
+  int candidate;
+
+  ret = nxsem_wait(&g_tlssem);
+
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  ret = -EAGAIN;
+
+  for (candidate = 0; candidate < CONFIG_TLS_TASK_NELEM; candidate++)
+    {
+      int mask = 1 << candidate;
+      if ((g_tlsset & mask) == 0)
+        {
+          g_tlsset |= mask;
+          g_tlsdtor[candidate] = dtor;
+          ret = candidate;
+          break;
+        }
+    }
+
+  nxsem_post(&g_tlssem);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: task_tls_destruct
+ *
+ * Description:
+ *   Destruct all TLS data element associated with allocated key
+ *
+ * Input Parameters:
+ *    None
+ *
+ * Returned Value:
+ *   A set of allocated TLS index
+ *
+ ****************************************************************************/
+
+void task_tls_destruct(void)

Review comment:
       > I will explain what I mean and also make some comments based on your 
answers.
   > 
   > > TLS here mean Task local storage, collide with thread local storage.:(
   > 
   > Let's use `task_ls_` and not `task_tls_` so the confusion will be removed. 
Do you agree?
   > 
   > > From the concept, task local storage is very similar to thread local 
storage, but at the task level.
   > 
   > Because I do not have visibility about conditions (when and how) of 
`task_tls_alloc` call, I can't get a full picture. Based on your example let's 
assume that it is a time when the library is instantiated for the first time. 
Then the allocated ID will be shared across the library instances while global 
data will be stored in Task LS. For example I have 2 different libraries that 
rely on top of Task LS. The library A attach `dtorA` at index 0 and library B 
attach `dtorB` at index 1. When one of tasks that use library A exits then 
`task_tls_destruct` is called and we will call `dtorB` from library A user 
context point of view. Yes, the `0` will be passed as a parameter, but I'm not 
sure if such situation is valid from isolation point of view. 
   
   Yes, it's possible. Another possibility is that the task use both libraries 
and then dtorA/dtorB get the different non-NULL pointer.
   
   > Even more, if there is a but in library A code and it somehow at some 
point pass a wrong index to `task_tls_set_value` then `dtorB` will be called 
with non-zero argument and that value can match with the same value that is 
allocated by library B users. This potentially can lead to security issues.
   > 
   
   Yes, the wrong usage will crash the system, but it doesn't increase the 
security risk, because the task local storage is used only in FLAT/PROTECTED 
build, not KERNEL build(The static/dynamic library get a new copy for each task 
in this case). Task in FLAT/PROTECTED mode already can access other tasks and 
do any evil thing.
   
   > > > We can have 0 .. 64 as a limit for CONFIG_TLS_TASK_NELEM and use up to 
uint64_t for index storage. Or add save task pid together with each allocated 
index so that can be reused in task_tls_destruct to filter destructors to call
   > > 
   > > 
   > > could you explanation more? The code is almost same as the thread local 
storage, just lift up one level.
   > 
   > There are actually two issues here:
   > 
   > 1. `CONFIG_TLS_TASK_NELEM` is not limited in Kconfig and there is next 
code:
   > 
   > ```
   > int task_tls_alloc(tls_dtor_t dtor)
   > {
   >   int ret;
   >   int candidate;
   > ...
   >   for (candidate = 0; candidate < CONFIG_TLS_TASK_NELEM; candidate++)
   >     {
   >       int mask = 1 << candidate;
   >       if ((g_tlsset & mask) == 0)
   >         {
   >           g_tlsset |= mask;
   >           g_tlsdtor[candidate] = dtor;
   >           ret = candidate;
   >           break;
   >         }
   >     }
   > ...
   >   return ret;
   > }
   > ```
   > 
   > so if `CONFIG_TLS_TASK_NELEM` is more then number of bits that `int` can 
carry `if ((g_tlsset & mask) == 0)` will start be `true` always and out of 
boundary array access will occur. Hopefully this will be caught by compiler. 
   
   @no1wudi please add range 0 64 to Kconfig for both CONFIG_TLS_NELEM and 
CONFIG_TLS_TASK_NELEM.
   
   > 2. Because of lack of use case description I was thinking that each 
library A user will call `task_tls_alloc()` at the start and will use that 
allocated index, but maybe it is not a use case (but that is a more like a 
traditional TLS). 
   
   Yes, you are right, each library need allocate the index when it need access 
the global variables at the first time.
    
   > So I was thinking that we can store a bit mask to keep tracking of indexes 
allocated by a task. Again even now I'm not aware about this API usage point of 
view assuming that it can be called by any task in the system.
   
   The API usage it's simple like the traditional TLS, @no1wudi will provide an 
usage in libuv like this:
   ```
   static void uv__global_free(void* global) {
     if (global) {
       uv_library_shutdown();
       uv__free(global);
     }
   }
   
   uv__global_t* uv__global_get(void) {
     static int index = -1;
     uv__global_t* global = NULL;
   
     if (index < 0) {
       index = task_tls_alloc(uv__global_free);
     }
   
     if (index >= 0) {
       global = (uv__global_t*)task_tls_get_value(index);
       if (global == NULL) {
         global = (uv__global_t*)uv__calloc(1, sizeof(uv__global_t));
         if (global) {
             global->once = UV_ONCE_INIT;
             global->uv__signal_global_init_guard = UV_ONCE_INIT;
             global->uv__signal_lock_pipefd[0] = -1;
             global->uv__signal_lock_pipefd[1] = -1;
             task_tls_set_value(index, (uintptr_t)global);
         }
       }
     }
   
     ASSERT(global != NULL);
     return global;
   }
   ```
   Basically, libuv define several global variables, but we have many services 
call libuv functions in the background. The above code support  the multiple 
libuv's instances with minimal change.
   
   1. uv__global_get allocate index and uv_global_t at the first call of the 
first task
   2. uv__global_get return the same global in the subsequent calls of the 
first task
   3. uv__global_get allocate a new global when the second task call libuv's 
function at the fist time
   4.  uv__global_get return the second global in the subsequent calls of the 
second task
   5. If task 3 never use libuv, the callback(uv__global_free) is still called 
that is why the NULL check exist in uv__global_free.
   
   I hope this case help you understand the usage.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscr...@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


Reply via email to