Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

445 lines
14 KiB

  1. We have a problem
  2. GDI has loaded a TrueType font of unknown origin and now it is
  3. time to put some glyphs on the screen. What do you do?
  4. In TrueType each glyph is defined by a program. The glyph bits are
  5. generated by running that program to construct an outline and then
  6. filling the resulting path. If you think that this sounds potentially
  7. dangerous, you would be right. It is ot hard to produce a font that can
  8. produce an exception during the rasterization process. Unfortunately,
  9. in the kernel, _try _excepts cannot save you in all cases. For example,
  10. in the kernel access of an invalid kernel mode address is not handled
  11. gracefully by _try _except; the result of accessing a bad kernel address
  12. is a blue screen.
  13. Sometimes GRE, or one of its components in the kernel need to call
  14. back to user space to perform dangerous task.
  15. Where do we draw the line between Kerneal and User?
  16. What is the interface to the user mode of the font driver? I will examine
  17. several options.
  18. (1) The Current DDI defines the interface between Kernel and User Mode.
  19. In this case we would place the entire font driver in user space
  20. and we would make calls to the font driver using the existing DDI
  21. interface.
  22. Pro: Very safe. All foreign code is removed from kernel.
  23. Con: Less efficient. This would require that all calls to the font
  24. driver would cost a context switch.
  25. Con: Callbacks are part of the DDI. For example, the font drivers
  26. return a glyph outline by calling PATHOBJ functions to form a
  27. path. If we were to retain this query method, then we would be
  28. forced to reproduce the PATHOBJ fucntionality in user space [1].
  29. (2) Modify the current font driver interface (as defined by the DDI) so
  30. that it is more appropriate as a User Mode interface.
  31. (3) Driver Defined Interface between Kernel and User Mode
  32. In this case, the font driver is split into two pieces: a kernel
  33. part and an user part. GRE talks to the kernel portion of the font driver
  34. using the current interface as defined in the DDI. However, GRE provides
  35. services to let the font driver call the user mode part of its driver.
  36. Con: Some of the font drvier remains in kernel mode and so we have not
  37. completely solved the problem. An errant driver can still kill
  38. the system.
  39. Pro: This is simple. GRE is not touched other than new code added to
  40. provide a mechanism for a font driver to call user space
  41. functions.
  42. _________
  43. [1] Unfortunately the user mode driver cannot use GDI path functions
  44. because this would invite deadlock.
  45. The Proposed Client-Server Architecture
  46. Nomeclature
  47. GDI
  48. The routines a provided by gdi32.dll. All of these routines
  49. are in user mode.
  50. GRE
  51. The kernel mode portion of the graphics engine.
  52. Server
  53. A user mode process that responds to requests
  54. made by client process. Most of the work done satisfying the
  55. client request is done in user mode so as to protect other
  56. processes.
  57. UMS
  58. "User Mode Server", the server process when in user mode.
  59. KMS
  60. "Kernel Mode Server", the server process when in kernel mode.
  61. Client
  62. A process, currently in kernel mode, that calls to the
  63. server process. Sometimes I will refer to this as the
  64. kernel client
  65. What is so tricky?
  66. The NT architecture complicates the client-server process because the user
  67. mode address space of the server process is not visible to the client
  68. process. However, the all valid kernel mode addresses are visible to both
  69. the client and server, once they are in kernel mode.
  70. The Strategy I.
  71. Communication between client and server is done in kernel mode. The grunt
  72. work is done in user mode in the address space of the server process.
  73. The client process finds out that he needs to call the server. The
  74. client creates a buffer in kernel memory, informs the server process where
  75. it is and then waits for the answer. The server process copies that
  76. message to the user memory where it is processed by the user mode portion
  77. of the server code. The server goes back to kernel mode and then copies
  78. the user mode result to the kernel. The client, who has been patiently
  79. waiting in the kernel, is then informed that his package is ready. The
  80. server waits in the kernel for the next request.
  81. The Message
  82. A message consists of two parts. The first part is the GDIMSG strcuture
  83. that contains information about the data buffer. The second part of the
  84. message is the data buffer, a contiguous block of memory. This data
  85. buffer is usually separate from the GDIMSG.
  86. typedef struct _GDIMSG {
  87. void *pv; // pointer to message buffer
  88. unsigned cj; // size of message buffer
  89. struct {
  90. void *pv; // pointer to `in' portion of buffer
  91. unsigned cj; // size of `in' portion of buffer
  92. } in;
  93. struct {
  94. void *pv; // pointer to `out' portion of buffer
  95. unsigned cj; // size of `out' portion of buffer
  96. } out;
  97. } GDIMSG;
  98. The Strategy II.
  99. When the user mode server process is started, it calls GdiEnableMessage to
  100. request a pointer to an initial message buffer of a size secified by the
  101. server process. GRE will allocate a buffer in the user mode memory of the
  102. user mode server (UMS), fill in the GDIMSG buffer provided by the UMS
  103. giving the address of the requested user mode buffer. The UMS
  104. /*****************************************************************
  105. * We need to add a couple fields to the TEB *
  106. * *
  107. * typedef struct _TEB { *
  108. * ... *
  109. * GDI_MESSAGE *pMsg; // pointer to client *
  110. * ... *
  111. * } TEB; *
  112. *****************************************************************/
  113. /******************************Public*Routine******************************\
  114. *
  115. * Routine Name: vUserServerProcess
  116. *
  117. * Routine Description:
  118. *
  119. * This is the user more server process that processes
  120. * messages from the kernel mode part of the font
  121. * driver.
  122. *
  123. * Arguments: none
  124. *
  125. * Return Value: none
  126. *
  127. \**************************************************************************/
  128. void vUserServerThread(void)
  129. {
  130. GDI_MESSAGE Msg;
  131. while ( GdiGetMessage( &Msg ) == NO_ERROR )
  132. {
  133. ProcessMessage( &Msg );
  134. }
  135. }
  136. /******************************Public*Routine******************************\
  137. *
  138. * Routine Name: NtGdiGetMessage
  139. *
  140. * Routine Description:
  141. *
  142. * Server mode thread routine that transports messages between
  143. * kernel mode and user mode.
  144. *
  145. * This routine does two things. It returns a message from
  146. * the user mode font driver to the kernel mode font driver
  147. * and goes to sleep. After it is awakend by a signal from
  148. * the kernel mode font driver, this routine sends a new
  149. * message to the the user mode server.
  150. *
  151. * Arguments:
  152. *
  153. * pUserMsg address of an GDI_MESSAGE structure residing
  154. * in user space.
  155. * GRE will allocate a buffer and
  156. * initialize it. Then it will fill the
  157. * GDI_MESSAGE structure to tell the
  158. * user mode font server process where the
  159. * locations of the `in' and `out' parts of the
  160. * message buffer.
  161. *
  162. * Return Value:
  163. *
  164. * If the routine was successful then it will return NO_ERROR.
  165. *
  166. \**************************************************************************/
  167. NT_STATUS NtGdiGetMessage( GDI_MESSAGE *pUserMsg )
  168. {
  169. void *pv;
  170. unsigned dp;
  171. TEM *pteb = NtCurrentTeb();
  172. // If the kernel memory has been allocated then there is an application
  173. // expecting an answer. We copy the message from the message buffer
  174. // to the kernel buffer where it becomes visible to the application
  175. // thread that is currently asleep in the kernel mode portion of the
  176. // font driver. When this is finished copying to the kernel buffer
  177. // we wake the application thread and go to sleep awaiting the
  178. // wakeup call the next application call.
  179. // If this is the first call from the server thread,
  180. // then there is no message buffer. In that case
  181. // we do not unload the message to the kernel buffer and
  182. // we do not signal the application
  183. if ( pUserMsg->cj )
  184. {
  185. // An application thread is asleep in the kernel mode portion of
  186. // the font driver. It is awaiting some data from the User side.
  187. // We fill the kernel buffer from the a portion of the message
  188. // buffer and then free the user buffer.
  189. CopyMemory(
  190. pteb->pMsg->out.pv, // destination address
  191. pUserMsg->out.pv, // source address
  192. pUserMsg->out.cj // size in bytes
  193. );
  194. // the server process is done with the user message so we
  195. // can free up the associated memory
  196. FreeUserBuffer( pUserMsg->pv );
  197. // Wake up the client thread
  198. SignalEvent( pteb->ClientEvent );
  199. }
  200. // Go to sleep until woken by the client thread.
  201. WaitForEvent( pteb->ServerEvent );
  202. // <<< APPLICATION WAKES SERVER THREAD UP HERE >>>
  203. // OK, we're awake!
  204. // The application has left a message in pteb->pKernelThread which
  205. // points into a single contiguous data buffer in kernel memory.
  206. // It is our job to copy that block of data over to user memory
  207. // and then to fix up a user message that corresponds to that
  208. // block of memory in user space.
  209. // Copy the applications's message to the TEB. Remember that in this
  210. // nomenclature, that the "message" contains pointers to data. It
  211. // does not contain the data itself. Note that at this point, the
  212. // pointers in the user message are wrong. They point to the
  213. // kernel data which will not be accessable by the user process.
  214. pteb->UserMsg = *(pteb->pMsg);
  215. // Allocate memory in user space to receive a copy of the message
  216. // data that now resides in kernel memory. In practice, this step
  217. // will consist to see if we already have enough space allocated
  218. // in a dedicated chunk of memory. If it is big enough, we use it,
  219. // otherwise we create a look-aside chunk of memory and use it
  220. // temprorarily.
  221. if (!(pteb->UserMsg.pv = AllocUserBuffer( pteb->UserMsg.cj )))
  222. {
  223. return( ERROR_NOT_ENOUGH_MEMORY );
  224. }
  225. // Fix up the message pointers
  226. //
  227. // Now that we have allocated space for the message date in
  228. // User mode memory, we must correct the User message so that
  229. // the pointer's to the various parts of the buffer are correct.
  230. // Since the user buffer is a copy of the kernel buffer, all the
  231. // pointers are off by the same amount. This amount is equal
  232. // to the numerical difference between the pointer to start
  233. // of the kernel message buffer and the start of the user
  234. // message buffer.
  235. dp = (BYTE*) UserMsg.pv - (BYTE*) pMsg->pv;
  236. (BYTE*) UserMsg.pv += dp; // correct the pointers
  237. (BYTE*) UserMsg.in.pv += dp; // in the user message
  238. (BYTE*) UserMsg.out.pv += dp; // directory
  239. // The
  240. // we do the copy after the pointers have been corrected.
  241. CopyMemory(
  242. UserMsg.in.pv, // destination
  243. pteb->pMsg.in.pv, // source
  244. UserMsg.in.cj // size in bytes
  245. );
  246. // Fill in the caller's message header
  247. *pUserMsg = pteb->UserMsg;
  248. return( STATUS_SUCCESS );
  249. }
  250. /******************************Public*Routine******************************\
  251. *
  252. * Routine Name: SomeRandomFontDriverKernelRoutine
  253. *
  254. * Routine Description:
  255. *
  256. * This is the routine that is called when GRE makes a call
  257. * to the font driver. This routine runs in the `application'
  258. * thread.
  259. *
  260. * Arguments: Depends upon the call
  261. *
  262. * Return Value: Depends upon the call
  263. *
  264. \**************************************************************************/
  265. SomeRandomFontDriverKernelRoutine(FONTOBJ *pfo)
  266. {
  267. // cjIn and cjOut are known by this point
  268. GDI_MESSAGE Msg;
  269. Msg.cj = size_of_message_buffer;
  270. if ( Msg.pv = EngAllocMem( 0, Msg.cj, 'gmtt') )
  271. {
  272. // fill out the message directory and the associated buffer
  273. InitMessage( &Msg );
  274. // send the message to the font server in user space
  275. if ( EngSendMessage( pfo, &Msg ) == NO_ERROR )
  276. {
  277. ProcessMessage( &Msg ); // process the returned message
  278. }
  279. EngFreeMem( Msg.pv ); // free the kernel message buffer
  280. }
  281. }
  282. /******************************Public*Routine******************************\
  283. *
  284. * Routine Name: EngSendMessage
  285. *
  286. * Routine Description:
  287. *
  288. * Engine Service provided to kernel mode font clients for sending
  289. * and receiving messages from the server portion of the font
  290. * driver residing in user mode. This routine runs in the
  291. * `application' thread.
  292. *
  293. * Arguments:
  294. *
  295. * pfo pointer to a FONTOBJ. This identifies
  296. * the font driver
  297. * pMsg A pointer to a GDI_MESSAGE structure
  298. * supplied by the calling application thread
  299. * in the kernel.
  300. *
  301. * Return Value:
  302. *
  303. * NT_SUCCESS upon success.
  304. *
  305. \**************************************************************************/
  306. NTSTATUS EngSendMessage( FONTOBJ *pfo, GDI_MESSAGE *pMsg )
  307. {
  308. NTSTATUS rc;
  309. TEB *pteb;
  310. if (!(pteb = GreGetServerThread(pfo)))
  311. {
  312. rc = ERROR_CAN_NOT_COMPLETE;
  313. }
  314. else
  315. {
  316. pteb->pMsg = pMsg;
  317. SignalEvent( pteb->ServerEvent ); // wake up server thread
  318. WaitForEvent( pteb->ClientEvent ); // sleep until server
  319. // returns
  320. rc = ERROR_SUCCESS;
  321. }
  322. return( rc );
  323. }