From 6e7db049f5a31790b523aaea37d04f1abdab0e33 Mon Sep 17 00:00:00 2001 From: myteron Date: Thu, 6 Jun 2024 13:52:42 +0100 Subject: [PATCH 1/6] PyDoc2GitHub - Introduction to Multithreading and Multiprocessing in Python As part of #531 , converted Confluence page to markdown. Fixed missing links in main readme.md added dummy readme.md for CWE-665 added prerequisite read info box to 410, 833, 400, 392, 665 code example names have been converted to generic names references updated to current common theme. Signed-off-by: Helge Wehder Signed-off-by: myteron --- .../CWE-664/CWE-400/README.md | 4 + .../CWE-664/CWE-410/README.md | 4 + .../CWE-664/CWE-665/README.md | 9 + .../CWE-664/CWE-833/README.md | 4 + .../CWE-703/CWE-392/README.md | 4 + .../compliant01.py | 24 ++ .../compliant02.py | 15 + .../example01.py | 21 ++ .../image01.png | Bin 0 -> 38004 bytes .../image02.png | Bin 0 -> 15148 bytes .../noncompliant01.py | 15 + .../noncompliant02.py | 24 ++ .../noncompliant03.py | 23 ++ .../readme.md | 302 ++++++++++++++++++ docs/Secure-Coding-Guide-for-Python/readme.md | 4 +- 15 files changed, 451 insertions(+), 2 deletions(-) create mode 100644 docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-665/README.md create mode 100644 docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/compliant01.py create mode 100644 docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/compliant02.py create mode 100644 docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/example01.py create mode 100644 docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/image01.png create mode 100644 docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/image02.png create mode 100644 docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant01.py create mode 100644 docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant02.py create mode 100644 docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant03.py create mode 100644 docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/readme.md diff --git a/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-400/README.md b/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-400/README.md index d2ea79e0..2ca8035a 100644 --- a/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-400/README.md +++ b/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-400/README.md @@ -2,6 +2,10 @@ Canceling the task in a thread pool only prevents it from being executed if it has not started yet. For the task to be interruptible, it must handle the `threading.Event` flag. +> [!NOTE] +> Prerequisite to understand this page: +> [Intro to multiprocessing and multithreading](../../Intro_to_multiprocessing_and_multithreading/readme.md) + ## Non-Compliant Code Example Tasks can be submitted to the ThreadPoolExecutor by calling `submit()`. Submitted tasks can be canceled by calling `cancel()` on the Future object returned by `submit()`. Calling this method will return True and stop the task from being executed if it has not started yet. However, if its execution has already started, calling `cancel()` will instead return False and will not stop the task [[Python 3.10.4 docs on threading.Event]](https://docs.python.org/3/library/threading.html#event-objects). diff --git a/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-410/README.md b/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-410/README.md index 0caa2551..1fe12b0d 100644 --- a/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-410/README.md +++ b/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-410/README.md @@ -8,6 +8,10 @@ Thread pools combine: * Protecting the input queue from being overloaded by providing optional time-out functions * Handling of failures that occur in a thread without impacting the re-usability of threads [Brownlee 2022] +> [!NOTE] +> Prerequisite to understand this page: +> [Intro to multiprocessing and multithreading](../../Intro_to_multiprocessing_and_multithreading/readme.md) + ## Non-Compliant Code Example (Thread-Per-Message) The `noncompliant01.py` code example demonstrates the Thread-Per-Message design pattern. Each request sent to MessageAPI results in a creation of a new thread. diff --git a/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-665/README.md b/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-665/README.md new file mode 100644 index 00000000..c9018674 --- /dev/null +++ b/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-665/README.md @@ -0,0 +1,9 @@ +> [!NOTE] +> Prerequisite to understand this page: +> [Intro to multiprocessing and multithreading](../../Intro_to_multiprocessing_and_multithreading/readme.md) + + + +> [!NOTE] +> Placeholder page for links, content still need to be moved into github +> \ No newline at end of file diff --git a/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-833/README.md b/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-833/README.md index 54ab4a5c..f187b8c5 100644 --- a/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-833/README.md +++ b/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-833/README.md @@ -2,6 +2,10 @@ The number of threads that can simultaneously run within a `ThreadPoolExecutor` is limited by the `_max_workers` parameter. Submitting tasks whose execution is dependent on other tasks submitted to the same `ThreadPoolExecutor` may result in a *thread-starvation* deadlock. An example of this type of deadlock is shown in the following article [Brownlee, 2021], which describes it as "Deadlock 1: Submit and Wait for a Task Within a Task". Submitting a task will add it to an internal `ThreadPoolExecutor` queue. The task will be removed from the queue when one of the worker threads becomes available, i.e., after finishing its current task. If all workers are busy and all their current tasks are waiting for results from tasks that are waiting in the queue, the program will run indefinitely as no worker will be able to complete its task and take one of the blocking tasks from the queue. +> [!NOTE] +> Prerequisite to understand this page: +> [Intro to multiprocessing and multithreading](../../Intro_to_multiprocessing_and_multithreading/readme.md) + ## Non-Compliant Code Example (Interdependent Subtasks) The `noncompliant01.py` code example shows how a thread-starvation deadlock could happen when one task depends on the results of other, nested tasks. The `ReportTableGenerator` class creates a table out of the input data by building each row concurrently. The client can call the `generate_string_table()` method by providing a list of String arguments. Each argument is parsed in a separate thread that executes the inner method `_create_table_row()`. Creation of the row may consist of multiple steps, such as reformating the string, which could be performed in separate sub-threads. The `_reformat_string()` method is submitted within `_create_table_row()` and uses the same executor. Before the row is built, all of the building threads must return the results. diff --git a/docs/Secure-Coding-Guide-for-Python/CWE-703/CWE-392/README.md b/docs/Secure-Coding-Guide-for-Python/CWE-703/CWE-392/README.md index 41234b18..66886294 100644 --- a/docs/Secure-Coding-Guide-for-Python/CWE-703/CWE-392/README.md +++ b/docs/Secure-Coding-Guide-for-Python/CWE-703/CWE-392/README.md @@ -2,6 +2,10 @@ Failure to provide a mechanism for reporting that tasks in a thread pool failed as a result of an exceptional condition can make it difficult or impossible to diagnose the problem. +> [!NOTE] +> Prerequisite to understand this page: +> [Intro to multiprocessing and multithreading](../../Intro_to_multiprocessing_and_multithreading/readme.md) + **ThreadPoolExecutor** from the `concurrent.futures` module is used in Python to asynchronously execute callable tasks [[Python Docs - concurrent.futures]](https://docs.python.org/3/library/concurrent.futures.html). The `ThreadPoolExecutor` suppresses exceptions raised by tasks to ensure that the failure of a task does not result in the failure of a worker thread in the thread pool. Otherwise, worker threads would close whenever an exception is raised, negatively impacting the performance of the `ThreadPoolExecutor`. ## Non-Compliant Code Example (submit) diff --git a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/compliant01.py b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/compliant01.py new file mode 100644 index 00000000..d61eb3cf --- /dev/null +++ b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/compliant01.py @@ -0,0 +1,24 @@ +""" Compliant Code Example """ +import time +from multiprocessing import Process + + +def waste_time(t: int): + for _ in range(t): + _ += 1 + + +BIG_NUMBER = 100000000 +start = time.time() +waste_time(BIG_NUMBER) +end = time.time() +print(f"Time taken when executing sequentially (in seconds): {end - start}") +p1 = Process(target=waste_time, args=(BIG_NUMBER // 2,)) +p2 = Process(target=waste_time, args=(BIG_NUMBER // 2,)) +start = time.time() +p1.start() +p2.start() +p1.join() +p2.join() +end = time.time() +print(f"Time taken when executing in 2 processes (in seconds): {end - start}") \ No newline at end of file diff --git a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/compliant02.py b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/compliant02.py new file mode 100644 index 00000000..c75d9db4 --- /dev/null +++ b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/compliant02.py @@ -0,0 +1,15 @@ +""" Compliant Code Example """ +import asyncio + + +async def func(x: int): + print(f"{x} - start") + await asyncio.sleep(1) + print(f"{x} - end") + + +async def run_async(): + await asyncio.gather(func(1), func(2), func(3)) + + +asyncio.run(run_async()) \ No newline at end of file diff --git a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/example01.py b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/example01.py new file mode 100644 index 00000000..5dd177f6 --- /dev/null +++ b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/example01.py @@ -0,0 +1,21 @@ +""" Non-compliant Code Example """ + +import threading + + +def critical_func(x: str, l: threading.Lock): + print(f"{x}: performing regular operations") + with l: + print(f"{x}: entered critical section") + i = 0 + for _ in range(100000): + i += 1 + print(f"{x}: exiting critical section") + print(f"{x}: finished") + + +lock = threading.Lock() +t1 = threading.Thread(target=critical_func, args=("A", lock)) +t2 = threading.Thread(target=critical_func, args=("B", lock)) +t1.start() +t2.start() \ No newline at end of file diff --git a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/image01.png b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/image01.png new file mode 100644 index 0000000000000000000000000000000000000000..c3a9dc1436471f2363f775760df26f56273d131d GIT binary patch literal 38004 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 literal 0 HcmV?d00001 diff --git a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/image02.png b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/image02.png new file mode 100644 index 0000000000000000000000000000000000000000..4bb7ab3be681aea7dd85eb2801ca63d585326124 GIT binary patch literal 15148 zcmeIZbySq$*EULngrFkbNK1E@bRz-|rGmiFDGdT5Dh<*NLrHftAQA!+LpRbP-TmG3 zd)K?ppJ%P}u5;G;&N_deN_r9)cU(Z)Z+?Ghf+33wFRDFSjlS0qM%ep;oZE&1kWEhDZh3_L3!AT{6Xz; zEVMvD5$jNSA*<(Ux;u~EK)lx2en{xk5&5_)h@!%LAJa$p>?eWp$ke;9Occ$P(Nnps z6xzIJ#S5O5Z@=a!R&oeEH>}7u)L~TANNc{IOm|KElY#qi!|4vbF};zNo}QjOpT3{E ze>>yWs~Y=ECHC3VH=mEpz@y65Z}N}I;E_EX^$U0b_LJ;?qo?YjC9s=&0r$=9(0u2W z?)Xe_0b44WQhtcN=6zFt<|0X3K~&{((^iBMt>1U=lT95{29?-+>|DhNRctCJne9b* zfY(>pS;;F*I%@F`>hhrvIk{t=sf-2_2+#2lknyymywG*R|KD$8{)fSYLHBmb;hN_Q zK8{ylC;QZFeAbh#5M0gwVw*hnGp^2dQ@f*R&HVGAcZX2{Fl<`iijiC8SXgEx>K7Pv zJ(Sj8nNcZHozkJ!@T>X+x_i%K^Li+1ZTri5HwPBofSS)@w^|w z7$p%~H}x(|z@j%A>zq*~)*pgJ7n`3PaDTn{=wr(XLT!!d)I8l|!HXp*-pHYmDc$dE z8oJ=M!3m$?Ig{`28P$?D`A=}4q7xN`NZp={3b<_!*UZ=y=e@Q{7reW>9176P47l_1 zilD`7D{no6i2eQa1fi?O8NZ(9I@kocacsL}p17QZ-^FW69R3oRGm$5ph~lqOT)vduRjF1|`quYL>rogTLCzS_)>KVA23*KfO-dXO#0`q=luT_r zR(l>JH1#HU>MT}`D|y1uJc70TI9V}@CjIJsKU2(OFRkgrZpuUHn}d_>;_?Ya5c&C}r9zQ?j(>sBa5=XvF!-Pdx5$dypz^mC`igdkyB#n=h8bMiY!2 zl1WTG|6a6CuTNE)k7kF`q>z}njnwoqgu%>iw+%*2!uN5ho-9%?qVKr1es!x^l`iQ^CJypc{oj$v|CI6s^xguX5Z62D<2e7}Ed2){W z>~5CeSof(0{%7~5i>snqG<3cF>cGq#*_xMku`_2SsD$iRvefA5> zR`1hqr~YTvnO#C^m@EJKILZeyytj)=^r8nHE-?V0ho_lMKhXS?Flwi1frWRGTh4<$1` z%s}HxJ1eWghbp&!6n9tYqDoJ2KfS63XG!9$dar4x+vCGQ2e#IkKTY1x0IGd;{y*PK zD}Lfm&pN`X_j@ikGHG|d2bEl8c-p~l&nm+W|BOcGOwiBRD4nDmB?lCZdizuyhICc09k-vbNag; z-06OdT?n|pJ?S^Q{r`&$&@jVp1%n1ipKJgcnEc-`;{OmxO3t%s#Gpw+8=RH|vmE)^@c` zW>&S4;e%{FU3E}rubtBRW#RnO)xV*%k@}AznW};6+oJ_q?3milZ3p6-DFQ_;?x6hy zVA1K~X=lL>7K;taH&f(2*l@cIOugQ_(LApw5p&?K8mGL-hw8b{F zf^(uZI?{t|nVL*>lB^_ge-xKgsbVl=u(`ctP|o@YACkI;{Slgd-lWG%N!-yRPbf(| z-vSBZGIFb2He`dn93Ij!WM0Z%y8X?P`NDwfj>WL)UsZpO+S7gdFWGN0JTTGs#n+R} z5ePNz^aK!>%?}t-Pd4vG zfkx8%7cY+wDV!dam@w#3)0*G)ZvCMc{DH|jnZS^TqKsTUPg92E-NnilNP_VFs7Y|T zx$$LSBw5-Ix@A@|U*pz>6J%AW;Qcf(^NVi-r_wOg8C4XbLH1G>Po*uLDp1`NB@>jg zm6+Uiv&Ka#^vY@OYipap7RcI0p<;9#BMt-3UsnPe2@%LsPwk3)lFT9Xzv4;+(ox21 z8_;5$hsQ%5j><%G#Sc04FVA_chqJ72vlT)~h|tP&BgNR!2q3|R=NBtcQFL(=(3C5-}Hfv8;w@h&qHb z>Gd~fJmT2$=DjAjNfQr+^JkV7Hte{SsdiN=HvypjzsQacI`%uCC!L3TMwGoRQ3@oh zpZ{Pq=NpyvWl#4;nu)~lO<)b^R==!7_gjv)Ek~P{g7B8BD@t7Om0c^SgHX_ZpZ#CNm*v2Ph{mubH0j`qSb?_CQP{6YDMeI$8RN)VU1TRmgD#Qn{&xwu_$ zC68NyI4A~;N!bdY92`I~|CSI9zdJ2=UW^+zFp~_*W4kp0d83BR8$;XD+8pixDeII& z91Ilv8p{$?b6&D)8M6Qg9$9XO8H9%nT~5{u^bS=}E`8&Sa15h5{<+O&cLi@*O_QP$ zhU|5b7(K@ltqzytHEB$mNk9`Tmjvxuu*X0En^VY_8SN47R!!PLa-_)emkTZ&Ih$J8 zSm@CAdghUcFmt8`hUjy*1AVK*-!Ku55mB?v zlawJ7aY%*1i2uVXy>`-6Jg7V0_aPcx_X0rzBT zve`-RF|b+^-(-uj@>DN&J`{hBMghTRJ>g>{J}-Df`_BUauqYoLp1UA9NlyL&hF78= zV0kz-Nrz!FU+kaAIKc@mZ{E@QxN7%WC2-a$w996_aB4qS;ZQIyW%Hd_yjd6KbtTp4SyL%{|J48dX|GO z)Uf~Hym9HX&m3x6ky~o8IfaC$Q`6~$(Mo_y>{uP3cR!8+7UOY7PC0gqKGtk}y06jWtzI$@sECLYVXb7n(nK0MQ3hh0a2iZn`Krj};H=k62 zr{}A5o_{|_mbV>Q;?zsnR}@H7`&{=&HJ zkYNb@&6F4}9_i|FxL%h97{){w*ux6|ei5gWX0#^x?EBqqvkuL98InE|^Is+5v*J6h zO8XF;#~&8`&TFo=iznv^(we`GhJ-BzpX-}X10-l%o%(ct{(YheqEIIn0bpsr8k35y zkp&6^8_(IwRzaqwr1wc79tDY+@1fh6j#8cisfj?wlV`^Mmup*ViDr=>=?GPSR1!d> zt=WE$xbFZqqptS6^2PR8(MvDrC}-;lqR8ib@6jg#p}QBc&uUEpk;!3N0g0{(Ad_Qj z2WlxJ#&c#X zbFM>OC>L`eWF(xdQBqgdS9jO@|Fv>+uyU*j_}PptH2HmkitC`LmDkql1fV@+=gxPR z8wCK+@TY^$14Y18)(m!(ocQ1U`Z88-2H)%(M^+6(O`?v5Xu%0X#?r?1l&zXk&9Ku6 zvw&N}y36&{0uYYemU!9`Z@#xbF?s@(W5+&&occzBi+frY!ct%t{OEh*-NdfSEpv@hCtUSP*_|fnaTEQ1U8`l!T z++Ec-)QXi~qS1n!6+`Ciq#AeYcL&hUeq%-OOTla>%XoPt;0DT^bJ(A@JhVjqesTlg zw*XY#e6Z-^o9!sFf0{BsgSqta9Ws6P8dpSj^s(hJXrG%e#y%^u=R=7h#ti882e67I zP9s?DqwEf*FwFq(hv4Kw9GV1(P0k$#BGKINo2nLpE<^nMwfBm}AO|`3p6~(6+3=grT#Sq#4Wf*sLpVh3 zbS`_QgFfW@&gb(U1t6rYdpeC#ROoXCChp@+5=Otqz1S zeJu0)_Pcm{s9a(ELlXRZmYPIA{~65jM5p5MQIc@7yuspd5K><1SaNZY>qT)1il_FN zAJ=rIdH$urc=~x{|(jn4)!>T%qxs9k8=riRYN^b3_ z*yQU!7bIXW+#3jS`1N@) z9c&w?Y3IwWb>0e<-Ow>`0R5^kJebA=*kaeNRDofUge0neSwdgk@nmAgx9fhqg2N}; ze%zPq+1{U)rrQ2HT?ciF}4VD{`{8D`Aq216gVf(x0%l|CS|X2BSWovfKLr#9wE5h zN3Ls*FO%;IKG9{yzJ4A-dwh}~>8p$aPka5B`42fMzae8hRw0m`3c6)VuX;2hP4PB;YCNBCOYdr6d;W~0lWW3dLs3t+6mD29R z@BQuK><{XV>K#UKbo>^xiC^rI@14(W`!i8z3i^V5uW=L_A zkLt3f)0O^27Oh>->esABCqQxej#Ne0tn3YnKFV_OO`8MhtzR`+wr;m>w)+_Ds3o$W zPSn~?mbg6{R3%;f2e2QfBYl6Q)YYcp{zvqOy>)Xg{dNF1Mc%6=>j{1MH+As`DViF# z0pMZDlIA3*- z&wB5aR)f?OHv*bmbugin$gIzPYc*1R69>vtm2P)RJYTO}Y3)Rx*oS{f1hi5T3mzNk z9+t-g{95|B%2kW^cjwdKQ#1aEZlp~kwdr(FrC%2jkzC8Lk@aKYlSOmZ49mS{OZvJW zwClEoU;@zLv`Tl~*tB|g`S`$RZLyTXfF0K3q2E-Pn5b?73)Rh*<}gXB21+g?f(9CK z?6A}xI1SzvPwhIu`+8$A-5ja#v-p5zAg}eMmZV_h{y`_U_AzagXyoMk{5b-!*z(xOv9Qlt?a zTUOoVR^j$09`o+*8>2u0T_FPp=k{N;{KKy`^?mHc7RZCE&u(ZzD0!j{n`KHcWJrDc znY?OmzFCRp2nboft~)BF0MVJI@%i^?_MP-9p%RtzFN@4;ASw<#6#rKtLQ>bYAqoD& zTy*MTCg3+7LnN;)Vhls!#%e&-r`~yUb8$GWqeT7mnogQN04V8dg9d&VrF>$`f~oLCA1}WvG~ZBT-d?*zohSvTy1f8x7SvTis4_(Hy}TGZ}cuW z0X^$Gs-6hjZy{cd&#W~yrd7sPSoY<>ggfuW=BuGP-OM{Q?^c1Sn1Ja>xe_O8QDnTNufaCiq2t)9y-_N>=cQ*`ZnllwPCa!UaC zIY3vo1YOw&SEjeUVROAa?rZd0-_vn}$_NRR>^JhVf$FQvVce0EmwyZs;EC9Qy(nabm}kGEd|G+87~dm@rTG~j z7Z1ZAzNi15*k&pURr&gr9?H+gfyT_t014#)NuRT{e{*>fk!7Y*Vaw}dQqqO2-m(VeasueUHuHvr+k3e0F;5cNB%T`@?mP8tu+0z{yo zJ+wdA*n9eYBP|T6-L*s=Q|aO31NpPe70iM9+W@0zLito92Izx&g5@O~0Ig}5O5F18 zDgoZ}3Yqzx%ivqxZkbY)RRC0eBk9k5FJbS_-46V&xGY@f8G7_Y7nk8^ za?+;J|GZkA{izTFIWf>{>P?tbhJIyAP>zy3GjwBHs@1jpu8Nsryr+ZJUe7oI@c54q{60_4N#K$XYppOh@|)!hIEsStxKg}A zA^CF%Q8E+R3{V2}*jxUCv{I~T-Z|s$emeROd4t^RYuFL?jU^xKGGOo@gFUUjvvFr@n zQ*8utY^NHMt=yA`rtNmfphY)CHyj~_5)Kv4yr0HmyEUWRk+#U=xdQ8d;Dwi&%qz4SbNMFVsJ_gk zx!Z$9@bjLAaRIpa{Y^CG6gEs6XNNd5Cz_G*AH*$65W0EiJ^R+v3Fo%+%@Ia~JljR2 zZ=T+!*7;&UK-zvw;{9uV+$rGJPOV?T@>`=LxFDKJWL`84HM=! zjDE15Y{bOJHo^F0B}gNyJtP55X0grBe4U!Fuq9J4#HY^d*CVADSc;0uA4h>;(ePzs zYH}q?n&!Tm8W)>5NdNWw_!?wq?SiwBK&-QS8-5$J^&Y%-q2dm~yek9xL=4o`4mo)x zzSz9yydA^Y12lIC$6M6wK|^H^jn6Ny=}Pk+R!?;`m^m&!pcqEmh&(lzN~G<{=7>>%1DGIp1gMu#%+xGw(mwKY9 zH8}@XE-=8MIGIq;1tWh_qmHE5mc900>yZgJrw?G{p&xo@ zHs9o~2EY8{xZJ91&ib7nYwKiGE2>7ZUEFD@{dbW65n-7<2638ES`Tt6o0hnd=Bk;N z2}ahOG$xtF>Ll_G=j*2UqNVsg;e?tNs9!Wwmt5`}p)#<{vp=b30E zWYQWf3W+AVlPVuRjiaL6irNu?Myd|5m0IY`TB0BZNBW|zMB>S{1o_e>a_L3>2BqF> zWd~vRPtnL?gphCzTF6<17njyS#4cCkc5IwnF0VU$j?C$T8AfeOoT689w6F;3bS__m z5}_CKSKlEHKR#UNs>nGbCbIo9xqt_ewA`j8(FsJ zm1*{?%C?uTxSDp1j!%2*3D{Asd3)h_N3kvMgXD0%Fs|QFv$Nnxd8Pp&OsDX#j~|%U z+5%#j%S4RS-r!f`_ZUpLd6uH-3m)S(|^YG;Q z!Xj&v>rUD`qDcv~YveRGXjc{Xs4Tlil?%8Uc&F0dWV{Lah7O?=B`o?yx5@133H$@^ zsL_J#>!z6@oa`}D4=#>x3XbNmHiWjKjvJ~rFiQROU}GNsasDbB#)-wF525k!mchdx zx5(4-gFo#`+dP$Y&WjKeb6EWb2vYe48QwNk*vYKb`%Z*}eDEh)61?;N*SpYPUmm#& z#w`VDSmN3|K9+k(PF5@AmBr7*&M(R59uz}${p>gqBHkpux)sbt8NYEjCgPdb$rzFa4_i>18@H!cBW3KfTf2oE$?40K zRl0p$_uYUfsnPLk&3kR6JI}u-=cK+AY$?L=7Quo@AN!)7ylVjTEhoN8pGfvVqYHgQ z+N9TIl?jFy$v;Fm<_}oY#!(7^4PV33OIzL7anp~D?TRE}GIE(#T+0?8E!-@kB?`Uh zM=1cXax=;@!scWoPGQFru`>ttF)W3+$)-}WuFLG|mDW~e&`O>UhG5wV3rp}ugNSK& z#1wwXM7RTfS|_m>tMALv0HAOxKpcLw&)H22 zQ-r?zqtio!x_84*qYrgqW`ha{N3|vo1Vd+oAQ}N0LlfT<-{|fwq2Go?O)C0~yNG2u ze^-PGi-H()dYn{4*=UL!DW4I}tT1}?78NGC4Nn8Bu2C<_QIXDy7K+Q%+fapwZXmGI z%3_t$6MrK!knQ*=hq@EvOye=?T)7-R8c=pS7{Rtlp_< zVbtM$|MTX}NLgAptc{IQQC-^?*^IP1GV~b}q4$@W_h04Q+3|DzMLKeV zW&Hr9$ZeNv%8LIeclV8>j4ULrkBe-43VTHX_$&%=qFG-!51df4;Lu4v@pMurme>?h z2>jF>6=x7F9nGy{O>eL3`OX&$vmCfYn2#`|#4pKAv_+(vlA}F%SZ{%A$lXohZwxj& zaa!*^j(YnPF|PbcbphRrk5#}?qH!offcxphwVXVfXfx7JY=cKbuL=o{k-k%DuM;VW)r}v^53`yWEbu%2uFeGF08yzJ6 z#34N^8u_R4_EWgaaF(ol@M6;VX9r?pu_zn84pIUl*m(vHXd+KYMHg7op&pD2^U-djfGD&+8vdB-mNMi&u65TjzvXY_I$ z{S=XX=r^}Tq5yfepY_oN&;g|dne5OLRr>G*?W_8i*4bHFwm2QQJHy*DvY+jB4rLFV z!O;4b)q2u{H04nYg;i<%rT3XFp}UKq^)YY%iyTS}Yo!aZ=K%vXqY{py-2AlpS7JjM+msii6^0#TOH9@Q9 zd4)I@uhpViqV)@{SFZ+=9x*4pA%BW;Y9jnVB#AsK;R%8Pnoy(cFB8%+)>Kza&<}aA zkDil>u*&o!IX(kfuIIZ8!S8Y{am`>xcpn|nWXPHOA#jgl?1OH#nwfh_K+)^G2OwP_j)iYyVe46^ zA#De%oMVQgiy7Fp<8GI*vt$y|tpde~@+;|Ej z$vrk_2W9k|6o>zOy4(5C5j$q=wwn`1?sq~yskrbH#lL%oCo;sLV4ag47ae}=v&*9x zTxOUNU=x8aGsZPMl-A)k@az|g>D9NmlL0-!yx^)?Mu;x-!P{ZKjQZ6$EMN-bSk=U!Rnq3Fm!3)0}bHxDE7YRPX};IT>A=qel3w1!cDR$lrW*u#c^q$<5f z`m0rLGvyb-BNmMmJ=0uh1HVA94XyWPc7ts87)=Zbxz#WHNSKDxB2l*^?=4p4jY6WKJfF^W(`_-|e1PW9AJHDs+@htY5X(GHqgzOI*8}~Tt=N_-f6sGxX(nEQ!|O;O%E5)Pa9siSkxi-c@6 z9$*wc>vcAlgz&Iijc1?Lx^4{WWNVuR1}~W-M|ilOsrGX#UT`RvQl7*t?BH>E?vohV z#Q^JIS>Z+W%o7ANsx-fG3`qsp86`r@plhl?$$3`FRNd4a_INyXQa?bx#$Jhx6=c__ zaP$H~N87{ae{*r9TjY;LXETg6RVzkPkz+;!DKW1_AP=cA21NmvZsYHlw~y#rz({(; z>?FxN^6S6}C@5k1t3dC|9cf#wCGvHeoP5Nn2m^&Sr$tX!O`8t9*#`l}h?z)>(Gh~! zyvF)6>nQ{100t#sys!8-mjuF7)bJ*athT_d8Ws|q`mwGi;d_ccIOZlXGk6F*P6~`$ zAc?zqLBI7$mjxQ*6^UdCt^(08N>z^i5&Ni}VD~HD!D4`xM!XTjO%Z=v^#q*KH)a+L z`@<|yaSY1m_?iJ}EqIvLvE_z2LBb_JKcJmI$*ru6$&#K|;@bZ{Zs1JOr; zG?szndYh(QW*NtY|0erxt_3KoBX57^vB^{T0Wpm+Vtt;|Han}c-KFUhkldvNZ6Rzt zl{C<_iHEovH90(ypBxMU;!zsQuNnjz0}1pR2!5(aZ-t)ssuzl2+vOW09+FQ!@zG{u z29z8cj@ILE!m+l#;I`YqLV%fodlyJYybnLAc7EQImru!|be}9UF@Hx^mN#zw9(HZv zznizb1aMXqu6%|ob@@L<6{oRQpK~%w8{y}IpzslRgHcJYkekiipG1lj^N2!NvgZOY!WQQJ6^$Y_^`u`&-2sLNi;S9&hC2+kRB5|i+g>zssPB=w5>)zu4B7Mk7o;%z4E~FB z3jHRRityKag)P0#c5XX4UDf)3RZ~EbKdO0cma;O_SNq*rH++lE&=Va%t zg~qvgf~j9m!w)cF{3W6Rp-VOldd*!V(_7v;@F`J?sKd3+Oca%1(uEykYX&sIZnBSs zJT(@>66%v-s>A|Gj|%cipNF$S2i1*ZQ=-aZFz&syD}FPl3P5;e&mSVJ+_Tz+SYM1A zMBCtCQXb*4^KX7*sx^RxAeC|mBhCWHqc71?wi<(S?`fZ+g2VX^3tu)Z{RaO7ixux;wf5bS98DC$9tw~&CQLP`eD;J2DfpkR%=`{YCq1y@pqH}{S{FD0$f=>Jg{+Fd6(7; zygb`9C0dipd%N;iB~+UWdIK$8f*FB_{~dkCY^TM!=I_A}lB+0}NqrleUWvvilwjj6 z57mHuq*ES9o&Lby|KZyn)p%i7Hr%UJo!Fv5fP6F}I24wUTl{3yi?36Uh9|l^>09c1 zjO*~VHxp?$&NqiWN;I+zfB-8wapfEwZVZ@g{GD(7TQz|1v# zy5b0RXYToh2mIPZ59Q;D!qsnD2`e8j?U#7w4IcoN^5&rJrA;FG{PnF}x*ixfj?r1x zW@WOX&u;HHYOE`q7i}jy;fN&(23wlovRK%tyCxy5*=NV)&>faLXAw0y{#5L9t5s zk}=Rx2)S#-Ic`5aCuGluNxQy12cI>|aaw}_K(Xp*Qn@qrizqGb_o$qc<# z(IS%u2(tNSz*n@= zXO;OmDIsR20?3XDiR$8Rm^rhLG*Wh{3$V?Fk~D%7@KYR&o>&fK>#yg8aecccuJHhq zUfb~MY#D0;y-}WG3c*ujkw%ny54U>Q`&fx&)W4+PI$$x@Dzo$t=p zx4_(&O`oD;;eVOfp9t^c-E-`EZeL2k8BycH!zso2rF84201>E9)+XHz8%rs&h0NL% zm*&KNGJTSAW{0X1T-cT6{uXIyO3{SyLGXeYZH#{FAyvvFU6q$Xbm0ypa|JB>+anO3 z7))XAypPrg*=L_89t4Rr@z&Q-?juqI&2z%u+u}=&U4etGe{9wM-evGX&58c?tsY+bZs| zKYe(^U8_{X#j_AhUJMx(!DHtnyD?ov`B0Q%^)2NS30D4EaSQ!ajUxS5@gJ#wRbJAf zw~`-^LvTgI-LuvcldE87;akN;p0*nC=*D42FER5V(rOL>YscaO#~Og%MLfV6_kZ~* zbye)q{iq(0NOgN6sYi#i<@?q0(*aP(2eR&S_bo(T%L^MyW{s5Z%<4(eZTENA0;B5u z@rgg^xmyn8wnuW6AYd9g8qPmxXv$to0Gx?x|JlXsGfr-3y3m|cCsKqT1OB+9*oh(l zthQ1e+f={}HtgIZC(E6L(&{G7A2iIn!`Zk;kOq@Z0B}-Gy|?o8F2{iljQ&nPz~p)P z>C^T89Un74idoaV^}XCSdtrrBpVa2kzSA8a?tWguv(dTpHON(4e&Ok*O_9Xs?G zVdWqI&hOqfePwN@2Eo#}a&RpPTQ4!YP1V0scv=6d^y;?u?A2{R9^|^*ao*UxTv+L4 zeSG;O!f(<$;It0XWIr~;6)-X-L^^;=eqg1+X)CfZ+6J6*cho>s|8~d zM&1kBBX1gnW$J;?diJ!F_kR$^|49n`fBwDyM~)+aaS-I*w%&i;z)0(RAd9>y{>E{P|AZ(%AQJWeCQ}@y%GN!K|J!H! V8~;`=!5ywBDhiq}%H-Yz{V&FiUGD$@ literal 0 HcmV?d00001 diff --git a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant01.py b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant01.py new file mode 100644 index 00000000..851bc606 --- /dev/null +++ b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant01.py @@ -0,0 +1,15 @@ +""" Non-compliant Code Example """ +import threading + + +def wait_for_other(x: str, other: threading.Thread): + print(f"{x}: waiting for other") + other.join() + print(f"{x}: finished waiting") + + +t = threading.Thread(target=wait_for_other, args=("A", threading.current_thread())) +t.start() +print("B: waiting for other") +t.join() +print("B: finished waiting") \ No newline at end of file diff --git a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant02.py b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant02.py new file mode 100644 index 00000000..f5f9a6ab --- /dev/null +++ b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant02.py @@ -0,0 +1,24 @@ +""" Non-compliant Code Example """ +import time +from threading import Thread + + +def waste_time(t: int): + for _ in range(t): + _ += 1 + + +BIG_NUMBER = 100000000 +start = time.time() +waste_time(BIG_NUMBER) +end = time.time() +print(f"Time taken when executing sequentially (in seconds): {end - start}") +t1 = Thread(target=waste_time, args=(BIG_NUMBER // 2,)) +t2 = Thread(target=waste_time, args=(BIG_NUMBER // 2,)) +start = time.time() +t1.start() +t2.start() +t1.join() +t2.join() +end = time.time() +print(f"Time taken when executing in 2 threads (in seconds): {end - start}") diff --git a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant03.py b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant03.py new file mode 100644 index 00000000..1de6cee1 --- /dev/null +++ b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant03.py @@ -0,0 +1,23 @@ +""" Non-compliant Code Example """ +import time +from threading import Thread + + +def waste_time(t: float): + time.sleep(t) + + +WAIT_TIME = 4 +start = time.time() +waste_time(WAIT_TIME) +end = time.time() +print(f"Time taken when executing sequentially (in seconds): {end - start}") +t1 = Thread(target=waste_time, args=(WAIT_TIME // 2,)) +t2 = Thread(target=waste_time, args=(WAIT_TIME // 2,)) +start = time.time() +t1.start() +t2.start() +t1.join() +t2.join() +end = time.time() +print(f"Time taken when executing in 2 threads (in seconds): {end - start}") \ No newline at end of file diff --git a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/readme.md b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/readme.md new file mode 100644 index 00000000..eaec1dcf --- /dev/null +++ b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/readme.md @@ -0,0 +1,302 @@ +# Introduction to Multithreading and Multiprocessing in Python + +This page aims to explain the concepts that could be found in the following rules: + +- [CWE-410: Insufficient Resource Pool](../CWE-664/CWE-410/README.md) +- [CWE-833: Deadlock - Development Environment - eTeamSpace (ericsson.com)](../CWE-664/CWE-833/README.md) +- [CWE-400: Uncontrolled Resource Consumption](../CWE-664/CWE-400/README.md) +- [CWE-392: Missing Report of Error Condition](../CWE-703/CWE-392/README.md) +- [CWE-665: Improper Initialization](../CWE-664/CWE-665/README.md) + +## What is Multithreading in Python - Multithreading vs Multiprocessing + +![image01.png](image01.png "image01.png") + +Source: Tug of War: MultiProcessing Vs MultiThreading [Zaman 2021](https://medium.com/@noueruzzaman/tug-of-war-multiprocessing-vs-multithreading-55341c1f2103) + +**Multithreading** is the ability of a CPU to provide multiple threads of execution concurrently [Wikipedia 2024](https://en.wikipedia.org/wiki/Multithreading_(computer_architecture)). Usually, all instructions are performed sequentially in a single main thread. The multithreading approach allows the program to perform multiple tasks simultaneously. Multithreading should not be confused with **multiprocessing**. In multithreading, the threads share resources of a single or multiple cores - that includes the computing units, CPU caches, the translation lookaside buffer and memory space. Processes in the multiprocessing approach each have their own separate memory space and other resources. + +In python: + +- multithreading is handled by the `threading` module +- multiprocessing is handled by the `multiprocessing` module + +## Locks + +Sometimes we do not want multiple threads to reach the same part of the code at the same time. For instance, when threads share access to the same resource, we must ensure that said resource won't be edited by them at the same time in case one overwrites changes of the other (ex. when two threads write to the same file). Locks are a type of object that allows the programmer to mark a *critical section* of the code so that only the threads currently holding the lock can perform operations. It can be compared to passing around a microphone during a meeting so that only one person at a time can speak, thus preventing the conversation from becoming chaotic. +The example01.py code is depicting a simple use of a lock from the `threading` module: + +*[example01.py](example01.py):* + +```python +import threading + + +def critical_func(x: str, l: threading.Lock): + print(f"{x}: performing regular operations") + with l: + print(f"{x}: entered critical section") + i = 0 + for _ in range(100000): + i += 1 + print(f"{x}: exiting critical section") + print(f"{x}: finished") + + +lock = threading.Lock() +t1 = threading.Thread(target=critical_func, args=("A", lock)) +t2 = threading.Thread(target=critical_func, args=("B", lock)) +t1.start() +t2.start() +``` + +**Example output from `example01.py`:** + +```bash +A: performing regular operations +A: entered critical section +B: performing regular operations +A: exiting critical section +A: finished +B: entered critical section +B: exiting critical section +B: finished +``` + +Whenever one thread enters the critical section, no other thread can enter it until the thread in the critical section leaves it and relieves the lock. +See also *CWE-667: Improper Locking* [MITRE 2024](https://cwe.mitre.org/data/definitions/667). + +## Deadlock + +A deadlock is a situation when one or more threads are waiting for a situation that will never occur, meaning that the program will run indefinitely unless it is forcefully closed by the user as demonstrated in `noncompliant01.py`. `Python 3.13` introduced an option to disable `GIL` when launchign python via `PYTHON_GIL=0` or `-X gil=0` [GitHub swtaarrs 2024](https://github.com/python/cpython/pull/116338). + +*[noncompliant01.py](noncompliant01.py):* + +```python +""" Non-compliant Code Example """ +import threading + + +def wait_for_other(x: str, other: threading.Thread): + print(f"{x}: waiting for other") + other.join() + print(f"{x}: finished waiting") + + +t = threading.Thread(target=wait_for_other, args=("A", threading.current_thread())) +t.start() +print("B: waiting for other") +t.join() +print("B: finished waiting") + +``` + +The `join()` is a method that makes it so that the thread where the method was called will wait for the thread on which the method was called to finish. The "finished waiting" prints will never happen. + +## Global Interpreter Lock (GIL) + +GIL is a specific type of mutex (lock) that allows only one thread to hold control of the Python interpreter. This means that only one thread can be executed at a time, even in a multi-threaded architecture with more than one CPU core as demonstrated in `noncompliant02.py` + +*[noncompliant02.py](noncompliant02.py):* + +```python +""" Non-compliant Code Example """ +import time +from threading import Thread + + +def waste_time(t: int): + for _ in range(t): + _ += 1 + + +BIG_NUMBER = 100000000 +start = time.time() +waste_time(BIG_NUMBER) +end = time.time() +print(f"Time taken when executing sequentially (in seconds): {end - start}") +t1 = Thread(target=waste_time, args=(BIG_NUMBER // 2,)) +t2 = Thread(target=waste_time, args=(BIG_NUMBER // 2,)) +start = time.time() +t1.start() +t2.start() +t1.join() +t2.join() +end = time.time() +print(f"Time taken when executing in 2 threads (in seconds): {end - start}") + +``` + +**Example output from `noncompliant02.py`:** + +```bash +Time taken when executing sequentially (in seconds): 5.477974891662598 +Time taken when executing in 2 threads (in seconds): 5.985692262649536 +``` + +Using multiple threads, each going through half as many iterations ended up being slower than running all calculations in the main thread. The additional execution time comes from the thread overhead, that is the operations performed in order to create, run and end a new thread. +In order to avoid issues caused by GIL, one can use processes instead of threads, as each process, among other resources, has its own Python interpreter. + +*[compliant01.py](compliant01.py):* + +```python +""" Compliant Code Example """ +import time +from multiprocessing import Process + + +def waste_time(t: int): + for _ in range(t): + _ += 1 + + +BIG_NUMBER = 100000000 +start = time.time() +waste_time(BIG_NUMBER) +end = time.time() +print(f"Time taken when executing sequentially (in seconds): {end - start}") +p1 = Process(target=waste_time, args=(BIG_NUMBER // 2,)) +p2 = Process(target=waste_time, args=(BIG_NUMBER // 2,)) +start = time.time() +p1.start() +p2.start() +p1.join() +p2.join() +end = time.time() +print(f"Time taken when executing in 2 processes (in seconds): {end - start}") +``` + +**Example output from `compliant01.py`:** + +```bash +Time taken when executing sequentially (in seconds): 5.829047441482544 +Time taken when executing in 2 processes (in seconds): 3.0721683502197266 +``` + +## CPU bound vs I/O bound threads + +Threads can be separated into two categories depending on which factors have the biggest impact on their performance: + +- CPU bound - those threads are limited by the CPU performance (ex. doing complex calculations) +- I/O bound - those threads need to wait for Input/Output operations (ex. reading from a database + +`GIL` has a significantly greater impact on CPU-bound threads. While the threads wait for I/O, no additional Python code needs to be executed and the GIL is passed to threads that actively perform operations. + +We can simulate I/O bound threads by replacing calculations from the previous code example with `time.sleep`: + +*[noncompliant03.py](noncompliant03.py):* + +```python +""" Non-compliant Code Example """ +import time +from threading import Thread + + +def waste_time(t: float): + time.sleep(t) + + +WAIT_TIME = 4 +start = time.time() +waste_time(WAIT_TIME) +end = time.time() +print(f"Time taken when executing sequentially (in seconds): {end - start}") +t1 = Thread(target=waste_time, args=(WAIT_TIME // 2,)) +t2 = Thread(target=waste_time, args=(WAIT_TIME // 2,)) +start = time.time() +t1.start() +t2.start() +t1.join() +t2.join() +end = time.time() +print(f"Time taken when executing in 2 threads (in seconds): {end - start}") +``` + +Now threads no longer hold the GIL and the split sleep time can be "run" concurrently + +**Example output from `noncompliant03.py`:** + +```bash +Time taken when executing sequentially (in seconds): 4.002594947814941 +Time taken when executing in 2 threads (in seconds): 2.002474546432495 +``` + +## Asynchronous I/O + +Asynch IO is a form of input/output processing that allows achieving parallel code execution using a single thread. It uses the `async` and `await` Python keywords as well as the `asyncio` Python package. The main mechanism of asynch IO is **coroutines** - a function whose execution can be suspended before reaching return, which allows it to pass control to another coroutine. The `async` keyword is used to define a coroutine and the `await` keyword suspends its execution, allowing a different routine to run. Objects that can be used with `await` are called *awaitables*. The `asyncio` package provides additional functions, such as creating and managing *event loops* - objects responsible for running asynchronous tasks and callbacks [Python docs - event loop 2024](https://docs.python.org/3/library/asyncio-eventloop.html) as shown in `compliant02.py`. + +*[compliant02.py](compliant02.py):* + +```python +""" Compliant Code Example """ +import asyncio + + +async def func(x: int): + print(f"{x} - start") + await asyncio.sleep(1) + print(f"{x} - end") + + +async def run_async(): + await asyncio.gather(func(1), func(2), func(3)) + + +asyncio.run(run_async()) +``` + +The `compliant02.py` example contains definitions of two coroutines - `func()` and `run_async()`. The `asyncio.run()` function starts an event loop and schedules the provided coroutine. The `asyncio.gather` groups given awaitables together. Grouped awaitables can be executed concurrently, awaited or canceled. The `await.sleep()` function allows us to sleep the current task for a given number of milliseconds, giving control to other coroutines in the group. Despite not using multiple threads or processes, the code example is executed concurrently, which can be seen in the console output: + +**Example output from `compliant02.py`:** + +```bash +1 - start +2 - start +3 - start +1 - end +2 - end +3 - end +``` + +More information about asyncio can be found in the python documentation [Python docs - asyncio 2024](https://docs.python.org/3/library/asyncio.html). + +## Thread and Process Pools + +The creation of new threads and processes is considered a computationally expensive operation that may cause performance issues. In order to alleviate those issues, we can use a **thread/process** pool. Those pools are defined as "a group of per-instantiated and idle threads, which stand ready to be given work" [Tutorialspoint 2024](https://www.tutorialspoint.com/concurrency_in_python/concurrency_in_python_pool_of_threads.htm). Threads in a thread pool are called **worker threads**. The advantage of using worker threads over regular threads is the fact that upon completing its execution, a worker thread can be reused, saving time and other resources. +Classes used for managing thread/process pools are called Executors. These classes provide methods for the creation of thread/process pools, defining their sizes, submitting tasks, and terminating the worker threads/processes. In Python, the `Executor` is an abstract class that is implemented in two concrete subclasses: + +- `ThreadPoolExecutor` +- `ProcessPoolExecutot` + +ThreadPoolExecutor is a part of the `concurrent.futures` package, which is a high-level interface used for both multithreading and multiprocessing. +The relationship between the concurrency-related packages mentioned on this page is shown in the diagram below: + +![image02.png](image02.png "image02.png") + +Source: [PluralSight Ojo 2022](https://www.pluralsight.com/courses/python-concurrency-getting-started) + +## Modules used for Multithreading/Multiprocessing + +Here is a list of modules that are commonly used when writing applications using multithreading/multiprocessing: + +- `threading`: allows for manual creation and handling of threads [Python docs - threading 2024](https://docs.python.org/3/library/threading.html) +- `concurrent.futures`: a higher-level threading management interface, that provides thread and process pools, Future class for obtaining results asynchronously etc. [Python docs - concurrent.futures 2024](https://docs.python.org/3/library/concurrent.futures.html#module-concurrent.futures) +- `queue`: provides a thread-safe interface for exchanging data between running threads [Python docs - queue 2024](https://docs.python.org/3/library/queue.html#module-queue) +- `multiprocessing`: allows for the spawning of processes in a similar way threading is used for threads [Python docs - multiprocessing 2024](https://docs.python.org/3/library/multiprocessing.html#module-multiprocessing) + +## Bibliography + +||| +|:---|:---| +|[[Zaman 2021]](https://medium.com/@noueruzzaman/tug-of-war-multiprocessing-vs-multithreading-55341c1f2103)|Tug of War: MultiProcessing Vs MultiThreading. Available from: \[Accessed 6 June 2024]| +|[[Wikipedia 2024]](https://en.wikipedia.org/wiki/Multithreading_(computer_architecture))|Multithreading (computer architecture). Available from: \[Accessed 6 June 2024]| +|[[MITRE 2024]](https://cwe.mitre.org/data/definitions/667)|CWE-667: Improper Locking. Available from: \[Accessed 6 June 2024]| +|[[GitHub swtaarrs 2024]](https://github.com/python/cpython/pull/116338)|Allow disabling the `GIL` with `PYTHON_GIL=0` or `-X gil=0` GitHub pull request. Available from: \[Accessed 6 June 2024]| +|[[Python docs - event loop 2024]](https://docs.python.org/3/library/asyncio-eventloop.html)|Python docs - event loop 2024. Available from: \[Accessed 6 June 2024]| +|[[Python docs - asyncio 2024]](https://docs.python.org/3/library/asyncio.html)|Python docs - asyncio 2024. Available from: \[Accessed 6 June 2024]| +|[[Tutorialspoint 2024]](https://www.tutorialspoint.com/concurrency_in_python/concurrency_in_python_pool_of_threads.htm)|Concurrency in Python - Pool of Threads. Available from: \[Accessed 6 June 2024]| +|[[PluralSight Ojo 2022]](https://www.pluralsight.com/courses/python-concurrency-getting-started)|Getting Started with Python 3 Concurrency. Available from: \[Accessed 6 June 2024]| +|[[Python docs - threading 2024]](https://docs.python.org/3/library/threading.html)|threading — Thread-based parallelism. Available from: \[Accessed 6 June 2024]| +|[[Python docs - concurrent.futures 2024]](https://docs.python.org/3/library/concurrent.futures.html#module-concurrent.futures)|concurrent.futures — Launching parallel tasks. Available from: \[Accessed 6 June 2024]| +|[[Python docs - queue 2024]](https://docs.python.org/3/library/queue.html#module-queue)|queue — A synchronized queue class. Available from: \[Accessed 6 June 2024]| +|[[Python docs - multiprocessing 2024]](https://docs.python.org/3/library/multiprocessing.html#module-multiprocessing)|multiprocessing — Process-based parallelism. Available from: \[Accessed 6 June 2024]| diff --git a/docs/Secure-Coding-Guide-for-Python/readme.md b/docs/Secure-Coding-Guide-for-Python/readme.md index bd073fbb..c6fda3da 100644 --- a/docs/Secure-Coding-Guide-for-Python/readme.md +++ b/docs/Secure-Coding-Guide-for-Python/readme.md @@ -44,10 +44,10 @@ It is **not production code** and requires code-style or python best practices t |[CWE-197: Numeric Truncation Error](CWE-664/CWE-197/.)|| |[CWE-400: Uncontrolled Resource Consumption](CWE-664/CWE-400/README.md)|| |[CWE-409: Improper Handling of Highly Compressed Data (Data Amplification)](CWE-664/CWE-409/.)|| -|[CWE-410: Insufficient Resource Pool](CWE-664/CWE-410/.)|| +|[CWE-410: Insufficient Resource Pool](CWE-664/CWE-410/README.md)|| |[CWE-501: Trust Boundary Violation)](CWE-664/CWE-501/README.md)|| |[CWE-502: Deserialization of Untrusted Data)](CWE-664/CWE-502/.)|| -|[CWE-665: Improper Initialization](CWE-664/CWE-665/.)|| +|[CWE-665: Improper Initialization](CWE-664/CWE-665/README.md)|| |[CWE-681: Improper Control of a Resource Through its Lifetime](CWE-664/CWE-681/.)|| |[CWE-833: Deadlock](CWE-664/CWE-833/README.md)|| |[CWE-843: Access of Resource Using Incompatible Type ('Type Confusion')](CWE-664/CWE-843/.)|| From 79b1f239e88f9d038dd34f3292d2533fa47a6776 Mon Sep 17 00:00:00 2001 From: myteron Date: Fri, 7 Jun 2024 09:18:25 +0100 Subject: [PATCH 2/6] Fixed linting errors Signed-off-by: Helge Wehder Signed-off-by: myteron --- .../Secure-Coding-Guide-for-Python/CWE-664/CWE-665/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-665/README.md b/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-665/README.md index c9018674..ca3875fa 100644 --- a/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-665/README.md +++ b/docs/Secure-Coding-Guide-for-Python/CWE-664/CWE-665/README.md @@ -1,9 +1,10 @@ +# CWE-665: Improper Initialization + > [!NOTE] > Prerequisite to understand this page: > [Intro to multiprocessing and multithreading](../../Intro_to_multiprocessing_and_multithreading/readme.md) - +## Introduction > [!NOTE] > Placeholder page for links, content still need to be moved into github -> \ No newline at end of file From 7a2e52a2ef35eb6f3e107bc898e84b73eaa1bde6 Mon Sep 17 00:00:00 2001 From: myteron Date: Mon, 10 Jun 2024 09:25:54 +0100 Subject: [PATCH 3/6] Fixed typo and added SPDX Signed-off-by: myteron --- .../compliant01.py | 3 +++ .../compliant02.py | 2 ++ .../example01.py | 2 ++ .../noncompliant01.py | 2 ++ .../noncompliant02.py | 2 ++ .../noncompliant03.py | 2 ++ .../readme.md | 9 ++++++--- 7 files changed, 19 insertions(+), 3 deletions(-) diff --git a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/compliant01.py b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/compliant01.py index d61eb3cf..c5b39b2e 100644 --- a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/compliant01.py +++ b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/compliant01.py @@ -1,4 +1,7 @@ +# SPDX-FileCopyrightText: OpenSSF project contributors +# SPDX-License-Identifier: MIT """ Compliant Code Example """ + import time from multiprocessing import Process diff --git a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/compliant02.py b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/compliant02.py index c75d9db4..b76ad817 100644 --- a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/compliant02.py +++ b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/compliant02.py @@ -1,3 +1,5 @@ +# SPDX-FileCopyrightText: OpenSSF project contributors +# SPDX-License-Identifier: MIT """ Compliant Code Example """ import asyncio diff --git a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/example01.py b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/example01.py index 5dd177f6..b794c0cc 100644 --- a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/example01.py +++ b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/example01.py @@ -1,3 +1,5 @@ +# SPDX-FileCopyrightText: OpenSSF project contributors +# SPDX-License-Identifier: MIT """ Non-compliant Code Example """ import threading diff --git a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant01.py b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant01.py index 851bc606..0ffc03fc 100644 --- a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant01.py +++ b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant01.py @@ -1,3 +1,5 @@ +# SPDX-FileCopyrightText: OpenSSF project contributors +# SPDX-License-Identifier: MIT """ Non-compliant Code Example """ import threading diff --git a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant02.py b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant02.py index f5f9a6ab..72854d01 100644 --- a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant02.py +++ b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant02.py @@ -1,3 +1,5 @@ +# SPDX-FileCopyrightText: OpenSSF project contributors +# SPDX-License-Identifier: MIT """ Non-compliant Code Example """ import time from threading import Thread diff --git a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant03.py b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant03.py index 1de6cee1..31af01ff 100644 --- a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant03.py +++ b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/noncompliant03.py @@ -1,3 +1,5 @@ +# SPDX-FileCopyrightText: OpenSSF project contributors +# SPDX-License-Identifier: MIT """ Non-compliant Code Example """ import time from threading import Thread diff --git a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/readme.md b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/readme.md index eaec1dcf..f25b1ea3 100644 --- a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/readme.md +++ b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/readme.md @@ -64,11 +64,14 @@ B: finished ``` Whenever one thread enters the critical section, no other thread can enter it until the thread in the critical section leaves it and relieves the lock. -See also *CWE-667: Improper Locking* [MITRE 2024](https://cwe.mitre.org/data/definitions/667). +See also *CWE-667: Improper Locking* [[MITRE 2024](https://cwe.mitre.org/data/definitions/667)]. ## Deadlock -A deadlock is a situation when one or more threads are waiting for a situation that will never occur, meaning that the program will run indefinitely unless it is forcefully closed by the user as demonstrated in `noncompliant01.py`. `Python 3.13` introduced an option to disable `GIL` when launchign python via `PYTHON_GIL=0` or `-X gil=0` [GitHub swtaarrs 2024](https://github.com/python/cpython/pull/116338). +A deadlock is a situation when one or more threads are waiting for a situation that will never occur, meaning that the program will run indefinitely unless it is forcefully closed by the user as demonstrated in `noncompliant01.py`. + +>!INFO +>`Python 3.13` introduced an option to disable `GIL` when launching python via `PYTHON_GIL=0` or `-X gil=0` [[GitHub swtaarrs 2024](https://github.com/python/cpython/pull/116338)]. *[noncompliant01.py](noncompliant01.py):* @@ -273,7 +276,7 @@ The relationship between the concurrency-related packages mentioned on this page ![image02.png](image02.png "image02.png") -Source: [PluralSight Ojo 2022](https://www.pluralsight.com/courses/python-concurrency-getting-started) +Source: [[PluralSight Ojo 2022](https://www.pluralsight.com/courses/python-concurrency-getting-started)] ## Modules used for Multithreading/Multiprocessing From d4d4bc83f0acb1aac83152ee2bb0628ce08dbf9a Mon Sep 17 00:00:00 2001 From: myteron Date: Mon, 17 Jun 2024 16:32:52 +0100 Subject: [PATCH 4/6] Update docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/readme.md Co-authored-by: Georg Kunz Signed-off-by: myteron --- .../Intro_to_multiprocessing_and_multithreading/readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/readme.md b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/readme.md index f25b1ea3..cd4d8aa8 100644 --- a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/readme.md +++ b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/readme.md @@ -68,7 +68,8 @@ See also *CWE-667: Improper Locking* [[MITRE 2024](https://cwe.mitre.org/data/de ## Deadlock -A deadlock is a situation when one or more threads are waiting for a situation that will never occur, meaning that the program will run indefinitely unless it is forcefully closed by the user as demonstrated in `noncompliant01.py`. +A deadlock is a situation when one or more threads are waiting for a situation that will never occur, meaning that the program will run indefinitely unless it is forcefully closed by the user as demonstrated in `noncompliant01.py`. + >!INFO >`Python 3.13` introduced an option to disable `GIL` when launching python via `PYTHON_GIL=0` or `-X gil=0` [[GitHub swtaarrs 2024](https://github.com/python/cpython/pull/116338)]. From 4db023b38876422e134f9282b444e1ca85152033 Mon Sep 17 00:00:00 2001 From: Georg Kunz Date: Mon, 17 Jun 2024 17:38:49 +0200 Subject: [PATCH 5/6] Update docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/readme.md Signed-off-by: Georg Kunz --- .../Intro_to_multiprocessing_and_multithreading/readme.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/readme.md b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/readme.md index cd4d8aa8..a26ccb4e 100644 --- a/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/readme.md +++ b/docs/Secure-Coding-Guide-for-Python/Intro_to_multiprocessing_and_multithreading/readme.md @@ -70,7 +70,6 @@ See also *CWE-667: Improper Locking* [[MITRE 2024](https://cwe.mitre.org/data/de A deadlock is a situation when one or more threads are waiting for a situation that will never occur, meaning that the program will run indefinitely unless it is forcefully closed by the user as demonstrated in `noncompliant01.py`. - >!INFO >`Python 3.13` introduced an option to disable `GIL` when launching python via `PYTHON_GIL=0` or `-X gil=0` [[GitHub swtaarrs 2024](https://github.com/python/cpython/pull/116338)]. From 565d900209781becd6a08a47552f14ae6c56ec8a Mon Sep 17 00:00:00 2001 From: myteron Date: Mon, 17 Jun 2024 16:49:36 +0100 Subject: [PATCH 6/6] Update readme.md fixing linting Signed-off-by: myteron